scream-code 0.15.1 → 0.15.3

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.
@@ -4,7 +4,7 @@ import { dirname as __cjsShimDirname } from 'node:path';
4
4
  const __filename = __cjsShimFileURLToPath(import.meta.url);
5
5
  const __dirname = __cjsShimDirname(__filename);
6
6
  import "./suppress-sqlite-warning-C2VB0doZ.mjs";
7
- import { $t as isTodoItemShape, A as renderDiffLinesClustered, An as getInputHistoryFile, At as ENABLE_TERMINAL_FOCUS_REPORTING, B as BackgroundAgentStatusComponent, Bn as ScreamHarness, Bt as TERMINAL_THEME_LIGHT, Cn as PULSE_WAVE_FRAMES, Ct as createThemeStyles, D as toggleEmptySessionHint, Dn as saveTuiConfig, Dt as parseOsc11BackgroundTheme, E as isTurnElapsedEnabled, En as loadTuiConfig, Et as detectTerminalTheme, F as estimateTokens, Fn as CLI_USER_AGENT_PRODUCT, Ft as OSC11_RESPONSE_PREFIX_NO_ESC, G as getBreathingFrame, Gn as log, H as AgentGroupComponent, Hn as resolveScreamHome, Ht as isStreaming, I as getSharedSpeedTracker, In as PRODUCT_NAME, It as QUERY_TERMINAL_THEME, J as refineGoal, Jn as isOrphanedToolCallError, Jt as printableChar, K as resetBreathingClock, Kn as resolveGlobalLogPath, L as SkillActivationComponent, Ln as DEFAULT_CATALOG_URL, Lt as TERMINAL_FOCUS_IN, M as langFromPath, Mn as detectInstallSource, Mt as OSC11_QUERY, N as CachedContainer, Nn as CLI_COMMAND_NAME, Nt as OSC11_RESPONSE, O as ToolCallComponent, On as detectShellEnvironment, Ot as DISABLE_TERMINAL_FOCUS_REPORTING, P as ThinkingComponent, Pn as CLI_UI_MODE, Pt as OSC11_RESPONSE_PREFIX, Qt as formatErrorMessage, R as ReadGroupComponent, Rn as fetchCatalog, Rt as TERMINAL_FOCUS_OUT, S as getDaemonInstructions, Sn as PIXEL_PULSE_FRAMES, St as createMarkdownTheme, T as isEmptySessionHintDismissed, Tn as TuiLikePreferencesSchema, Tt as getColorPalette, U as WelcomeComponent, Un as MemoryMemoStore, Ut as FooterComponent, V as AssistantMessageComponent, Vt as isBusy, W as BREATHE_CYCLE_MS, Wn as flushDiagnosticLogs, Xn as SCREAM_ERROR_INFO, Xt as appendStreamingArgsPreview, Yn as ErrorCodes, Yt as STATUS_BULLET, Zt as argsRecord, _ as refreshUpdateCache, _n as isExperimentalFlagEnabled, _t as clearInfoPanelState, an as EMPTY_SESSION_HINT_URL, b as readJsonlFile, bn as isEmbeddingModelCached, bt as resolveThemeSync, c as hasDispose, cn as SESSION_TIPS, ct as handlePlanCommand, d as formatMemoryMemoForInjection, dn as getCtrlDHint, en as parseStreamingArgs, et as toTerminalHyperlink, fn as getLlmNotSetMessage, g as selectUpdateTarget, gn as sortSlashCommands, gt as supportsBalance, hn as BUILTIN_SLASH_COMMANDS, ht as refreshProviderBalance, i as buildRoleAdditionalText, in as CHARS_PER_TOKEN, j as highlightLines, jn as getLogDir, jt as ENABLE_TERMINAL_THEME_REPORTING, k as renderDiffLines, kn as getDataDir, kt as DISABLE_TERMINAL_THEME_REPORTING, l as isPlanExpandable, ln as TIP_ROTATION_INTERVAL_MS, mn as buildSkillSlashCommands, n as clearEvalPanelState, nn as stringValue, nt as getModelCycleLevel, on as EXIT_CONFIRM_WINDOW_MS, ot as handleFusionPlanCommand, pn as getNoActiveSessionMessage, q as clearGoalState, qn as isScreamError, r as openUrl, rn as truncateErrorMessage, s as disposeChildren, sn as MAIN_AGENT_ID, t as dispatchInput, tn as serializeToolResultOutput, tt as changeThinkingLevel, u as MoonLoader, un as getCtrlCHint, v as readUpdateCache, vn as setExperimentalFlags, w as UserMessageComponent, wn as TuiConfigParseError, wt as contrastTextHex, xn as startManualEmbeddingDownload, xt as createEditorTheme, y as appendJsonlLine, yn as getKnowledgeStore, z as parseReadGroupOutput, zn as saveCatalogCache, zt as TERMINAL_THEME_DARK } from "./dispatch-ATQMQq1p.mjs";
7
+ import { $t as isTodoItemShape, A as renderDiffLinesClustered, An as getInputHistoryFile, At as ENABLE_TERMINAL_FOCUS_REPORTING, B as BackgroundAgentStatusComponent, Bn as ScreamHarness, Bt as TERMINAL_THEME_LIGHT, Cn as PULSE_WAVE_FRAMES, Ct as createThemeStyles, D as toggleEmptySessionHint, Dn as saveTuiConfig, Dt as parseOsc11BackgroundTheme, E as isTurnElapsedEnabled, En as loadTuiConfig, Et as detectTerminalTheme, F as estimateTokens, Fn as CLI_USER_AGENT_PRODUCT, Ft as OSC11_RESPONSE_PREFIX_NO_ESC, G as getBreathingFrame, Gn as log, H as AgentGroupComponent, Hn as resolveScreamHome, Ht as isStreaming, I as getSharedSpeedTracker, In as PRODUCT_NAME, It as QUERY_TERMINAL_THEME, J as refineGoal, Jn as isOrphanedToolCallError, Jt as printableChar, K as resetBreathingClock, Kn as resolveGlobalLogPath, L as SkillActivationComponent, Ln as DEFAULT_CATALOG_URL, Lt as TERMINAL_FOCUS_IN, M as langFromPath, Mn as detectInstallSource, Mt as OSC11_QUERY, N as CachedContainer, Nn as CLI_COMMAND_NAME, Nt as OSC11_RESPONSE, O as ToolCallComponent, On as detectShellEnvironment, Ot as DISABLE_TERMINAL_FOCUS_REPORTING, P as ThinkingComponent, Pn as CLI_UI_MODE, Pt as OSC11_RESPONSE_PREFIX, Qt as formatErrorMessage, R as ReadGroupComponent, Rn as fetchCatalog, Rt as TERMINAL_FOCUS_OUT, S as getDaemonInstructions, Sn as PIXEL_PULSE_FRAMES, St as createMarkdownTheme, T as isEmptySessionHintDismissed, Tn as TuiLikePreferencesSchema, Tt as getColorPalette, U as WelcomeComponent, Un as MemoryMemoStore, Ut as FooterComponent, V as AssistantMessageComponent, Vt as isBusy, W as BREATHE_CYCLE_MS, Wn as flushDiagnosticLogs, Xn as SCREAM_ERROR_INFO, Xt as appendStreamingArgsPreview, Yn as ErrorCodes, Yt as STATUS_BULLET, Zt as argsRecord, _ as refreshUpdateCache, _n as isExperimentalFlagEnabled, _t as clearInfoPanelState, an as EMPTY_SESSION_HINT_URL, b as readJsonlFile, bn as isEmbeddingModelCached, bt as resolveThemeSync, c as hasDispose, cn as SESSION_TIPS, ct as handlePlanCommand, d as formatMemoryMemoForInjection, dn as getCtrlDHint, en as parseStreamingArgs, et as toTerminalHyperlink, fn as getLlmNotSetMessage, g as selectUpdateTarget, gn as sortSlashCommands, gt as supportsBalance, hn as BUILTIN_SLASH_COMMANDS, ht as refreshProviderBalance, i as buildRoleAdditionalText, in as CHARS_PER_TOKEN, j as highlightLines, jn as getLogDir, jt as ENABLE_TERMINAL_THEME_REPORTING, k as renderDiffLines, kn as getDataDir, kt as DISABLE_TERMINAL_THEME_REPORTING, l as isPlanExpandable, ln as TIP_ROTATION_INTERVAL_MS, mn as buildSkillSlashCommands, n as clearEvalPanelState, nn as stringValue, nt as getModelCycleLevel, on as EXIT_CONFIRM_WINDOW_MS, ot as handleFusionPlanCommand, pn as getNoActiveSessionMessage, q as clearGoalState, qn as isScreamError, r as openUrl, rn as truncateErrorMessage, s as disposeChildren, sn as MAIN_AGENT_ID, t as dispatchInput, tn as serializeToolResultOutput, tt as changeThinkingLevel, u as MoonLoader, un as getCtrlCHint, v as readUpdateCache, vn as setExperimentalFlags, w as UserMessageComponent, wn as TuiConfigParseError, wt as contrastTextHex, xn as startManualEmbeddingDownload, xt as createEditorTheme, y as appendJsonlLine, yn as getKnowledgeStore, z as parseReadGroupOutput, zn as saveCatalogCache, zt as TERMINAL_THEME_DARK } from "./dispatch-DGeogib-.mjs";
8
8
  import { a as setLocale, o as t } from "./text-input-dialog-BUXAKycx.mjs";
9
9
  import { createRequire } from "node:module";
10
10
  import { randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
@@ -32,7 +32,7 @@ function optionalBuildString(value) {
32
32
  return typeof value === "string" && value.length > 0 ? value : void 0;
33
33
  }
34
34
  const SCREAM_BUILD_INFO = {
35
- version: optionalBuildString("0.15.1"),
35
+ version: optionalBuildString("0.15.3"),
36
36
  channel: optionalBuildString(""),
37
37
  commit: optionalBuildString(""),
38
38
  buildTarget: optionalBuildString("darwin-arm64")
@@ -6553,27 +6553,34 @@ function installTerminalThemeTracking(state, onTheme) {
6553
6553
  }
6554
6554
  //#endregion
6555
6555
  //#region src/tui/components/chrome/pulse-wave-loader.ts
6556
- const FULL_BOX = "■";
6557
- const DIM_DOT = "⬝";
6556
+ /** Leading/trailing glyph: U+2588 (Block Elements — guaranteed single-column
6557
+ * width). The remaining stage cells render muted middle dots (U+00B7). */
6558
+ const LEADING_BOX = "■";
6558
6559
  /**
6559
- * 3-box pulse-wave loading indicator.
6560
+ * 8-box flowing pulse-wave loading indicator.
6560
6561
  *
6561
- * Cycles through a breathing wave pattern:
6562
- * ⬝ → ■ ⬝ → ⬝ ■ → (bounce back)
6562
+ * A leading box (full primary colour) sweeps across an 8-cell stage, trailed
6563
+ * by a dim box, over a row of muted middle dots:
6564
+ * ■ 淡 ■ · · · · · · → ■ ■ 淡 ■ · · · · → … → (bounce back)
6565
+ * (淡 shown here only to mark the trailing box; it renders as primary+dim.)
6563
6566
  *
6564
- * Colouring mirrors Grok's PromptLoadingBoxes:
6565
- * - active box (distance 0) → full primary colour
6566
- * - trailing box (distance 1) → ~72 % opacity via chalk dim
6567
- * - dim dot (distance 2) → muted
6567
+ * 14-frame ping-pong loop @ 80 ms — the leading edge never teleports, it
6568
+ * walks cell by cell.
6569
+ *
6570
+ * All glyphs are width-safe in terminal fonts (U+2588 Block Elements and
6571
+ * U+00B7 Latin-1) — the earlier ■/⬝ mix reflowed the whole line on every
6572
+ * frame because U+2B1D is not single-column guaranteed.
6568
6573
  *
6569
6574
  * The component auto-starts on construction. Call `stop()` to tear
6570
- * down the interval timer.
6575
+ * down the timer chain.
6571
6576
  */
6572
6577
  var PulseWaveLoader = class extends Text {
6573
6578
  currentFrame = 0;
6574
6579
  intervalId = null;
6575
6580
  ui;
6576
6581
  colorHex;
6582
+ /** Wall-clock time of the last frame advance, for drift-free scheduling. */
6583
+ lastTickAt = 0;
6577
6584
  /** Raw frame text without Text's full-width render padding, exposed so
6578
6585
  * callers can compose the wave inline with trailing content (e.g. the
6579
6586
  * reconnect label in the status bar). */
@@ -6589,39 +6596,60 @@ var PulseWaveLoader = class extends Text {
6589
6596
  this.start();
6590
6597
  }
6591
6598
  start() {
6599
+ this.lastTickAt = performance.now();
6592
6600
  this.updateDisplay();
6593
- this.intervalId = setInterval(() => {
6594
- this.currentFrame = (this.currentFrame + 1) % PULSE_WAVE_FRAMES.length;
6595
- this.updateDisplay();
6596
- }, 120);
6601
+ this.scheduleTick(80, 80);
6597
6602
  }
6598
6603
  stop() {
6599
6604
  if (this.intervalId) {
6600
- clearInterval(this.intervalId);
6605
+ clearTimeout(this.intervalId);
6601
6606
  this.intervalId = null;
6602
6607
  }
6603
6608
  }
6609
+ dispose() {
6610
+ this.stop();
6611
+ }
6612
+ /**
6613
+ * setTimeout self-rescheduling chain with drift-free frame advancement and
6614
+ * paint-cost backpressure (same pattern as the pi-tui Loader): a slow tick
6615
+ * defers the next one by `max(0, interval - cost, cost × 9)` instead of
6616
+ * stacking intervals, keeping the wave ≤ ~10% CPU under slow writes.
6617
+ */
6618
+ scheduleTick(intervalMs, delayMs) {
6619
+ const timer = setTimeout(() => {
6620
+ if (this.intervalId !== timer) return;
6621
+ const startedAt = performance.now();
6622
+ const elapsed = startedAt - this.lastTickAt;
6623
+ if (elapsed >= intervalMs) {
6624
+ const steps = Math.floor(elapsed / intervalMs);
6625
+ this.currentFrame = (this.currentFrame + steps) % PULSE_WAVE_FRAMES.length;
6626
+ this.lastTickAt += steps * intervalMs;
6627
+ this.updateDisplay();
6628
+ }
6629
+ const frameCostMs = performance.now() - startedAt;
6630
+ if (this.intervalId !== timer) return;
6631
+ const cadenceDelayMs = Math.max(0, intervalMs - frameCostMs);
6632
+ const backpressureDelayMs = frameCostMs * 9;
6633
+ this.scheduleTick(intervalMs, Math.max(cadenceDelayMs, backpressureDelayMs));
6634
+ }, delayMs);
6635
+ this.intervalId = timer;
6636
+ }
6604
6637
  setColorHex(colorHex) {
6605
6638
  this.colorHex = colorHex;
6606
6639
  this.updateDisplay();
6607
6640
  }
6608
6641
  updateDisplay() {
6609
6642
  const step = PULSE_WAVE_FRAMES[this.currentFrame] ?? PULSE_WAVE_FRAMES[0];
6610
- const cells = [
6611
- 0,
6612
- 1,
6613
- 2
6614
- ].map((idx) => this.renderCell(idx, step.active, step.forward));
6615
- this.frameText = cells.join(" ");
6643
+ const cells = Array.from({ length: 8 }, (_, idx) => this.renderCell(idx, step.active, step.forward));
6644
+ this.frameText = cells.join("");
6616
6645
  this.setText(this.frameText);
6617
6646
  this.ui.requestRender();
6618
6647
  }
6619
6648
  renderCell(index, active, forward) {
6620
6649
  const distance = forward ? active - index : index - active;
6621
- const glyph = distance >= 0 && distance < 2 ? FULL_BOX : DIM_DOT;
6622
- if (distance === 0) return chalk.hex(this.colorHex)(glyph);
6623
- if (distance === 1) return chalk.hex(this.colorHex).dim(glyph);
6624
- return chalk.dim(glyph);
6650
+ if (distance === 0) return chalk.hex(this.colorHex)(LEADING_BOX);
6651
+ if (distance === 1) return chalk.hex(this.colorHex).dim(LEADING_BOX);
6652
+ return chalk.dim("·");
6625
6653
  }
6626
6654
  };
6627
6655
  //#endregion
@@ -12397,7 +12425,7 @@ var ScreamTUI = class {
12397
12425
  const card = new OnboardingCardComponent({
12398
12426
  colors: this.state.theme.colors,
12399
12427
  onConfigure: () => {
12400
- import("./dispatch-Doe-DlpL.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
12428
+ import("./dispatch-CweYoXh3.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
12401
12429
  this.restoreEditor();
12402
12430
  onDone?.();
12403
12431
  });
@@ -14527,7 +14555,14 @@ function toHttpError(error) {
14527
14555
  if (isScreamError(error)) switch (error.code) {
14528
14556
  case ErrorCodes.GOAL_OBJECTIVE_EMPTY:
14529
14557
  case ErrorCodes.GOAL_OBJECTIVE_TOO_LONG:
14530
- case ErrorCodes.REQUEST_INVALID: return new HttpError(400, error.message, error.code);
14558
+ case ErrorCodes.REQUEST_INVALID:
14559
+ case ErrorCodes.SESSION_PERMISSION_MODE_INVALID:
14560
+ case ErrorCodes.SESSION_PLAN_MODE_INVALID:
14561
+ case ErrorCodes.SESSION_WOLFPACK_MODE_INVALID:
14562
+ case ErrorCodes.SESSION_MODEL_EMPTY:
14563
+ case ErrorCodes.SESSION_THINKING_EMPTY:
14564
+ case ErrorCodes.SKILL_NAME_EMPTY:
14565
+ case ErrorCodes.BACKGROUND_TASK_ID_EMPTY: return new HttpError(400, error.message, error.code);
14531
14566
  case ErrorCodes.GOAL_NOT_FOUND:
14532
14567
  case ErrorCodes.SESSION_NOT_FOUND: return new HttpError(404, error.message, error.code);
14533
14568
  case ErrorCodes.GOAL_ALREADY_EXISTS:
@@ -15696,6 +15731,49 @@ var WebSession = class {
15696
15731
  status: this.cachedStatus
15697
15732
  }, false);
15698
15733
  }
15734
+ /** Switch permission mode, then sync status + metadata (TUI /auto,/yes parity). */
15735
+ async switchPermission(mode) {
15736
+ if (!this.session) throw new HttpError(409, "会话已归档(只读),无法切换权限模式。");
15737
+ await this.session.setPermission(mode);
15738
+ this.permission = mode;
15739
+ await this.refreshStatus();
15740
+ this.broadcast({
15741
+ type: "status",
15742
+ status: this.cachedStatus
15743
+ }, false);
15744
+ if (this.homeDir) await saveMetadata(this.homeDir, this.getMetadata());
15745
+ }
15746
+ /** Toggle plan mode, then sync status (TUI /plan parity). */
15747
+ async switchPlanMode(enabled, strategy) {
15748
+ if (!this.session) throw new HttpError(409, "会话已归档(只读),无法切换计划模式。");
15749
+ await this.session.setPlanMode(enabled, strategy);
15750
+ await this.refreshStatus();
15751
+ this.broadcast({
15752
+ type: "status",
15753
+ status: this.cachedStatus
15754
+ }, false);
15755
+ }
15756
+ /** Toggle wolfpack mode, then sync status. */
15757
+ async switchWolfpackMode(enabled) {
15758
+ if (!this.session) throw new HttpError(409, "会话已归档(只读),无法切换 Wolfpack 模式。");
15759
+ await this.session.setWolfpackMode(enabled);
15760
+ await this.refreshStatus();
15761
+ this.broadcast({
15762
+ type: "status",
15763
+ status: this.cachedStatus
15764
+ }, false);
15765
+ }
15766
+ /** Toggle RLM persistent-python mode (+ optional max depth), then sync status. */
15767
+ async switchRlm(enabled, maxDepth) {
15768
+ if (!this.session) throw new HttpError(409, "会话已归档(只读),无法切换 RLM 模式。");
15769
+ await this.session.setRlmEnabled(enabled);
15770
+ if (maxDepth !== void 0) await this.session.setRlmMaxDepth(maxDepth);
15771
+ await this.refreshStatus();
15772
+ this.broadcast({
15773
+ type: "status",
15774
+ status: this.cachedStatus
15775
+ }, false);
15776
+ }
15699
15777
  getCoreSessionId() {
15700
15778
  return this.coreSessionId;
15701
15779
  }
@@ -15758,6 +15836,15 @@ var WebSession = class {
15758
15836
  await session.cancelGoal();
15759
15837
  }, true);
15760
15838
  }
15839
+ /**
15840
+ * Public handle to the live core Session for REST RPC forwarding.
15841
+ * Throws 409 for archived (read-only) sessions so handlers never call into a
15842
+ * closed core session instead of silently creating one.
15843
+ */
15844
+ requireLiveSession() {
15845
+ if (!this.session) throw new HttpError(409, "Session is archived (read-only)", ErrorCodes.SESSION_CLOSED);
15846
+ return this.session;
15847
+ }
15761
15848
  requireGoalSession(allowBusy = false) {
15762
15849
  if (!this.session) throw new HttpError(409, "Session is archived (read-only)", ErrorCodes.SESSION_CLOSED);
15763
15850
  if (!allowBusy && this.busy) throw new HttpError(409, "Session is busy", ErrorCodes.TURN_AGENT_BUSY);
@@ -16246,7 +16333,529 @@ var SessionManager = class {
16246
16333
  for (const [, ws] of this.sessions) await ws.close();
16247
16334
  this.sessions.clear();
16248
16335
  }
16336
+ /** Resolve a live session or throw 404/409; the single guard for all handlers. */
16337
+ getLiveSession(sessionId) {
16338
+ const ws = this.sessions.get(sessionId);
16339
+ if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
16340
+ return ws.requireLiveSession();
16341
+ }
16342
+ async getSessionStatus(sessionId) {
16343
+ return this.getLiveSession(sessionId).getStatus();
16344
+ }
16345
+ async getSessionUsage(sessionId) {
16346
+ return this.getLiveSession(sessionId).getUsage();
16347
+ }
16348
+ async getSessionContext(sessionId) {
16349
+ return this.getLiveSession(sessionId).getContext();
16350
+ }
16351
+ async getSessionPlan(sessionId) {
16352
+ return this.getLiveSession(sessionId).getPlan();
16353
+ }
16354
+ async setPermission(sessionId, mode) {
16355
+ const ws = this.sessions.get(sessionId);
16356
+ if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
16357
+ await ws.switchPermission(mode);
16358
+ }
16359
+ async setPlanMode(sessionId, enabled, strategy) {
16360
+ const ws = this.sessions.get(sessionId);
16361
+ if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
16362
+ await ws.switchPlanMode(enabled, strategy);
16363
+ }
16364
+ async clearPlan(sessionId) {
16365
+ await this.getLiveSession(sessionId).clearPlan();
16366
+ }
16367
+ async setWolfpackMode(sessionId, enabled) {
16368
+ const ws = this.sessions.get(sessionId);
16369
+ if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
16370
+ await ws.switchWolfpackMode(enabled);
16371
+ }
16372
+ async setRlm(sessionId, enabled, maxDepth) {
16373
+ const ws = this.sessions.get(sessionId);
16374
+ if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
16375
+ await ws.switchRlm(enabled, maxDepth);
16376
+ }
16377
+ async undoHistory(sessionId, count) {
16378
+ await this.getLiveSession(sessionId).undoHistory(count);
16379
+ }
16380
+ async compact(sessionId, instruction) {
16381
+ await this.getLiveSession(sessionId).compact(instruction ? { instruction } : {});
16382
+ }
16383
+ async listSkills(sessionId) {
16384
+ return this.getLiveSession(sessionId).listSkills();
16385
+ }
16386
+ async activateSkill(sessionId, name, args) {
16387
+ await this.getLiveSession(sessionId).activateSkill(name, args);
16388
+ }
16389
+ async removeSkill(sessionId, name) {
16390
+ await this.getLiveSession(sessionId).removeSkill(name);
16391
+ }
16392
+ async listPlugins(sessionId) {
16393
+ return this.getLiveSession(sessionId).listPlugins();
16394
+ }
16395
+ async getPluginInfo(sessionId, id) {
16396
+ return this.getLiveSession(sessionId).getPluginInfo(id);
16397
+ }
16398
+ async installPlugin(sessionId, source) {
16399
+ return this.getLiveSession(sessionId).installPlugin(source);
16400
+ }
16401
+ async setPluginEnabled(sessionId, id, enabled) {
16402
+ await this.getLiveSession(sessionId).setPluginEnabled(id, enabled);
16403
+ }
16404
+ async setPluginMcpServerEnabled(sessionId, id, server, enabled) {
16405
+ await this.getLiveSession(sessionId).setPluginMcpServerEnabled(id, server, enabled);
16406
+ }
16407
+ async removePlugin(sessionId, id) {
16408
+ await this.getLiveSession(sessionId).removePlugin(id);
16409
+ }
16410
+ async reloadPlugins(sessionId) {
16411
+ return this.getLiveSession(sessionId).reloadPlugins();
16412
+ }
16413
+ async activatePlugin(sessionId, id) {
16414
+ await this.getLiveSession(sessionId).activatePlugin(id);
16415
+ }
16416
+ async deactivatePlugin(sessionId, id) {
16417
+ await this.getLiveSession(sessionId).deactivatePlugin(id);
16418
+ }
16419
+ async injectPlugin(sessionId, id) {
16420
+ await this.getLiveSession(sessionId).injectPlugin(id);
16421
+ }
16422
+ async listMcpServers(sessionId) {
16423
+ return this.getLiveSession(sessionId).listMcpServers();
16424
+ }
16425
+ async getMcpStartupMetrics(sessionId) {
16426
+ return this.getLiveSession(sessionId).getMcpStartupMetrics();
16427
+ }
16428
+ async addMcpServer(sessionId, name, config) {
16429
+ await this.getLiveSession(sessionId).addMcpServer(name, config);
16430
+ }
16431
+ async reconnectMcpServer(sessionId, name) {
16432
+ await this.getLiveSession(sessionId).reconnectMcpServer(name);
16433
+ }
16434
+ async stopMcpServer(sessionId, name) {
16435
+ await this.getLiveSession(sessionId).stopMcpServer(name);
16436
+ }
16437
+ async removeMcpServer(sessionId, name) {
16438
+ await this.getLiveSession(sessionId).removeMcpServer(name);
16439
+ }
16440
+ async listBackgroundTasks(sessionId, activeOnly, limit) {
16441
+ return this.getLiveSession(sessionId).listBackgroundTasks({
16442
+ activeOnly,
16443
+ limit
16444
+ });
16445
+ }
16446
+ async getBackgroundTaskOutput(sessionId, taskId, tail) {
16447
+ return this.getLiveSession(sessionId).getBackgroundTaskOutput(taskId, tail !== void 0 ? { tail } : {});
16448
+ }
16449
+ async stopBackgroundTask(sessionId, taskId, reason) {
16450
+ await this.getLiveSession(sessionId).stopBackgroundTask(taskId, reason !== void 0 ? { reason } : {});
16451
+ }
16452
+ getConfig(options) {
16453
+ return this.harness.getConfig(options);
16454
+ }
16455
+ setConfig(patch) {
16456
+ return this.harness.setConfig(patch);
16457
+ }
16458
+ removeProvider(providerId) {
16459
+ return this.harness.removeProvider(providerId);
16460
+ }
16461
+ getExperimentalFlags() {
16462
+ return this.harness.getExperimentalFlags();
16463
+ }
16464
+ preflight() {
16465
+ return this.harness.preflight();
16466
+ }
16249
16467
  };
16468
+ async function handleSessionControlRoutes(req, res, url, method, manager) {
16469
+ const statusMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/status$`).exec(url);
16470
+ if (statusMatch && method === "GET") {
16471
+ try {
16472
+ sendJson(res, 200, await manager.getSessionStatus(decodeURIComponent(statusMatch[1])));
16473
+ } catch (error) {
16474
+ sendHttpError(res, error);
16475
+ }
16476
+ return true;
16477
+ }
16478
+ const usageMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/usage$`).exec(url);
16479
+ if (usageMatch && method === "GET") {
16480
+ try {
16481
+ sendJson(res, 200, await manager.getSessionUsage(decodeURIComponent(usageMatch[1])));
16482
+ } catch (error) {
16483
+ sendHttpError(res, error);
16484
+ }
16485
+ return true;
16486
+ }
16487
+ const contextMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/context$`).exec(url);
16488
+ if (contextMatch && method === "GET") {
16489
+ try {
16490
+ sendJson(res, 200, await manager.getSessionContext(decodeURIComponent(contextMatch[1])));
16491
+ } catch (error) {
16492
+ sendHttpError(res, error);
16493
+ }
16494
+ return true;
16495
+ }
16496
+ const planMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plan$`).exec(url);
16497
+ if (planMatch && method === "GET") {
16498
+ try {
16499
+ sendJson(res, 200, await manager.getSessionPlan(decodeURIComponent(planMatch[1])));
16500
+ } catch (error) {
16501
+ sendHttpError(res, error);
16502
+ }
16503
+ return true;
16504
+ }
16505
+ const permissionMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/permission$`).exec(url);
16506
+ if (permissionMatch && method === "POST") {
16507
+ try {
16508
+ const mode = requiredString(await readJsonBody(req), "mode");
16509
+ await manager.setPermission(decodeURIComponent(permissionMatch[1]), mode);
16510
+ sendJson(res, 200, { ok: true });
16511
+ } catch (error) {
16512
+ sendHttpError(res, error);
16513
+ }
16514
+ return true;
16515
+ }
16516
+ const planModeMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plan$`).exec(url);
16517
+ if (planModeMatch && method === "POST") {
16518
+ try {
16519
+ const body = await readJsonBody(req);
16520
+ const enabled = optionalBoolean(body, "enabled", true);
16521
+ const strategy = optionalString(body, "strategy");
16522
+ await manager.setPlanMode(decodeURIComponent(planModeMatch[1]), enabled, strategy);
16523
+ sendJson(res, 200, { ok: true });
16524
+ } catch (error) {
16525
+ sendHttpError(res, error);
16526
+ }
16527
+ return true;
16528
+ }
16529
+ const clearPlanMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plan/clear$`).exec(url);
16530
+ if (clearPlanMatch && method === "POST") {
16531
+ try {
16532
+ await manager.clearPlan(decodeURIComponent(clearPlanMatch[1]));
16533
+ sendJson(res, 200, { ok: true });
16534
+ } catch (error) {
16535
+ sendHttpError(res, error);
16536
+ }
16537
+ return true;
16538
+ }
16539
+ const wolfpackMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/wolfpack$`).exec(url);
16540
+ if (wolfpackMatch && method === "POST") {
16541
+ try {
16542
+ const enabled = optionalBoolean(await readJsonBody(req), "enabled", true);
16543
+ await manager.setWolfpackMode(decodeURIComponent(wolfpackMatch[1]), enabled);
16544
+ sendJson(res, 200, { ok: true });
16545
+ } catch (error) {
16546
+ sendHttpError(res, error);
16547
+ }
16548
+ return true;
16549
+ }
16550
+ const rlmMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/rlm$`).exec(url);
16551
+ if (rlmMatch && method === "POST") {
16552
+ try {
16553
+ const body = await readJsonBody(req);
16554
+ const enabled = optionalBoolean(body, "enabled", true);
16555
+ const maxDepth = typeof body["maxDepth"] === "number" ? Number(body["maxDepth"]) : void 0;
16556
+ await manager.setRlm(decodeURIComponent(rlmMatch[1]), enabled, maxDepth);
16557
+ sendJson(res, 200, { ok: true });
16558
+ } catch (error) {
16559
+ sendHttpError(res, error);
16560
+ }
16561
+ return true;
16562
+ }
16563
+ const undoMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/undo$`).exec(url);
16564
+ if (undoMatch && method === "POST") {
16565
+ try {
16566
+ const body = await readJsonBody(req);
16567
+ const count = typeof body["count"] === "number" ? Number(body["count"]) : 1;
16568
+ await manager.undoHistory(decodeURIComponent(undoMatch[1]), count);
16569
+ sendJson(res, 200, { ok: true });
16570
+ } catch (error) {
16571
+ sendHttpError(res, error);
16572
+ }
16573
+ return true;
16574
+ }
16575
+ const compactMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/compact$`).exec(url);
16576
+ if (compactMatch && method === "POST") {
16577
+ try {
16578
+ const body = await readJsonBody(req);
16579
+ await manager.compact(decodeURIComponent(compactMatch[1]), optionalString(body, "instruction"));
16580
+ sendJson(res, 200, { ok: true });
16581
+ } catch (error) {
16582
+ sendHttpError(res, error);
16583
+ }
16584
+ return true;
16585
+ }
16586
+ return false;
16587
+ }
16588
+ async function handleResourceRoutes(req, res, url, method, manager) {
16589
+ const skillsMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/skills$`).exec(url);
16590
+ if (skillsMatch && method === "GET") {
16591
+ try {
16592
+ sendJson(res, 200, await manager.listSkills(decodeURIComponent(skillsMatch[1])));
16593
+ } catch (error) {
16594
+ sendHttpError(res, error);
16595
+ }
16596
+ return true;
16597
+ }
16598
+ const skillActivateMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/skills/([^/]+)/activate$`).exec(url);
16599
+ if (skillActivateMatch && method === "POST") {
16600
+ try {
16601
+ const body = await readJsonBody(req);
16602
+ await manager.activateSkill(decodeURIComponent(skillActivateMatch[1]), decodeURIComponent(skillActivateMatch[2]), optionalString(body, "args"));
16603
+ sendJson(res, 200, { ok: true });
16604
+ } catch (error) {
16605
+ sendHttpError(res, error);
16606
+ }
16607
+ return true;
16608
+ }
16609
+ const skillRemoveMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/skills/([^/]+)$`).exec(url);
16610
+ if (skillRemoveMatch && method === "DELETE") {
16611
+ try {
16612
+ await manager.removeSkill(decodeURIComponent(skillRemoveMatch[1]), decodeURIComponent(skillRemoveMatch[2]));
16613
+ sendJson(res, 200, { ok: true });
16614
+ } catch (error) {
16615
+ sendHttpError(res, error);
16616
+ }
16617
+ return true;
16618
+ }
16619
+ const pluginsMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins$`).exec(url);
16620
+ if (pluginsMatch && method === "GET") {
16621
+ try {
16622
+ sendJson(res, 200, await manager.listPlugins(decodeURIComponent(pluginsMatch[1])));
16623
+ } catch (error) {
16624
+ sendHttpError(res, error);
16625
+ }
16626
+ return true;
16627
+ }
16628
+ const pluginInstallMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/install$`).exec(url);
16629
+ if (pluginInstallMatch && method === "POST") {
16630
+ try {
16631
+ const body = await readJsonBody(req);
16632
+ sendJson(res, 200, await manager.installPlugin(decodeURIComponent(pluginInstallMatch[1]), requiredString(body, "source")));
16633
+ } catch (error) {
16634
+ sendHttpError(res, error);
16635
+ }
16636
+ return true;
16637
+ }
16638
+ const pluginReloadMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/reload$`).exec(url);
16639
+ if (pluginReloadMatch && method === "POST") {
16640
+ try {
16641
+ sendJson(res, 200, await manager.reloadPlugins(decodeURIComponent(pluginReloadMatch[1])));
16642
+ } catch (error) {
16643
+ sendHttpError(res, error);
16644
+ }
16645
+ return true;
16646
+ }
16647
+ const pluginEnableMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/enable$`).exec(url);
16648
+ if (pluginEnableMatch && method === "POST") {
16649
+ try {
16650
+ const body = await readJsonBody(req);
16651
+ await manager.setPluginEnabled(decodeURIComponent(pluginEnableMatch[1]), decodeURIComponent(pluginEnableMatch[2]), optionalBoolean(body, "enabled", true));
16652
+ sendJson(res, 200, { ok: true });
16653
+ } catch (error) {
16654
+ sendHttpError(res, error);
16655
+ }
16656
+ return true;
16657
+ }
16658
+ const pluginMcpEnableMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/mcp/([^/]+)/enable$`).exec(url);
16659
+ if (pluginMcpEnableMatch && method === "POST") {
16660
+ try {
16661
+ const body = await readJsonBody(req);
16662
+ await manager.setPluginMcpServerEnabled(decodeURIComponent(pluginMcpEnableMatch[1]), decodeURIComponent(pluginMcpEnableMatch[2]), decodeURIComponent(pluginMcpEnableMatch[3]), optionalBoolean(body, "enabled", true));
16663
+ sendJson(res, 200, { ok: true });
16664
+ } catch (error) {
16665
+ sendHttpError(res, error);
16666
+ }
16667
+ return true;
16668
+ }
16669
+ const pluginActivateMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/activate$`).exec(url);
16670
+ if (pluginActivateMatch && method === "POST") {
16671
+ try {
16672
+ await manager.activatePlugin(decodeURIComponent(pluginActivateMatch[1]), decodeURIComponent(pluginActivateMatch[2]));
16673
+ sendJson(res, 200, { ok: true });
16674
+ } catch (error) {
16675
+ sendHttpError(res, error);
16676
+ }
16677
+ return true;
16678
+ }
16679
+ const pluginDeactivateMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/deactivate$`).exec(url);
16680
+ if (pluginDeactivateMatch && method === "POST") {
16681
+ try {
16682
+ await manager.deactivatePlugin(decodeURIComponent(pluginDeactivateMatch[1]), decodeURIComponent(pluginDeactivateMatch[2]));
16683
+ sendJson(res, 200, { ok: true });
16684
+ } catch (error) {
16685
+ sendHttpError(res, error);
16686
+ }
16687
+ return true;
16688
+ }
16689
+ const pluginInjectMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/inject$`).exec(url);
16690
+ if (pluginInjectMatch && method === "POST") {
16691
+ try {
16692
+ await manager.injectPlugin(decodeURIComponent(pluginInjectMatch[1]), decodeURIComponent(pluginInjectMatch[2]));
16693
+ sendJson(res, 200, { ok: true });
16694
+ } catch (error) {
16695
+ sendHttpError(res, error);
16696
+ }
16697
+ return true;
16698
+ }
16699
+ const pluginInfoMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/(?!(?:install|reload)$)([^/]+)$`).exec(url);
16700
+ if (pluginInfoMatch && method === "GET") {
16701
+ try {
16702
+ sendJson(res, 200, await manager.getPluginInfo(decodeURIComponent(pluginInfoMatch[1]), decodeURIComponent(pluginInfoMatch[2])));
16703
+ } catch (error) {
16704
+ sendHttpError(res, error);
16705
+ }
16706
+ return true;
16707
+ }
16708
+ if (pluginInfoMatch && method === "DELETE") {
16709
+ try {
16710
+ await manager.removePlugin(decodeURIComponent(pluginInfoMatch[1]), decodeURIComponent(pluginInfoMatch[2]));
16711
+ sendJson(res, 200, { ok: true });
16712
+ } catch (error) {
16713
+ sendHttpError(res, error);
16714
+ }
16715
+ return true;
16716
+ }
16717
+ const mcpMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp$`).exec(url);
16718
+ if (mcpMatch && method === "GET") {
16719
+ try {
16720
+ sendJson(res, 200, await manager.listMcpServers(decodeURIComponent(mcpMatch[1])));
16721
+ } catch (error) {
16722
+ sendHttpError(res, error);
16723
+ }
16724
+ return true;
16725
+ }
16726
+ const mcpMetricsMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/startup-metrics$`).exec(url);
16727
+ if (mcpMetricsMatch && method === "GET") {
16728
+ try {
16729
+ sendJson(res, 200, await manager.getMcpStartupMetrics(decodeURIComponent(mcpMetricsMatch[1])));
16730
+ } catch (error) {
16731
+ sendHttpError(res, error);
16732
+ }
16733
+ return true;
16734
+ }
16735
+ const mcpAddMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/add$`).exec(url);
16736
+ if (mcpAddMatch && method === "POST") {
16737
+ try {
16738
+ const body = await readJsonBody(req);
16739
+ await manager.addMcpServer(decodeURIComponent(mcpAddMatch[1]), requiredString(body, "name"), body["config"] ?? {});
16740
+ sendJson(res, 200, { ok: true });
16741
+ } catch (error) {
16742
+ sendHttpError(res, error);
16743
+ }
16744
+ return true;
16745
+ }
16746
+ const mcpReconnectMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/([^/]+)/reconnect$`).exec(url);
16747
+ if (mcpReconnectMatch && method === "POST") {
16748
+ try {
16749
+ await manager.reconnectMcpServer(decodeURIComponent(mcpReconnectMatch[1]), decodeURIComponent(mcpReconnectMatch[2]));
16750
+ sendJson(res, 200, { ok: true });
16751
+ } catch (error) {
16752
+ sendHttpError(res, error);
16753
+ }
16754
+ return true;
16755
+ }
16756
+ const mcpStopMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/([^/]+)/stop$`).exec(url);
16757
+ if (mcpStopMatch && method === "POST") {
16758
+ try {
16759
+ await manager.stopMcpServer(decodeURIComponent(mcpStopMatch[1]), decodeURIComponent(mcpStopMatch[2]));
16760
+ sendJson(res, 200, { ok: true });
16761
+ } catch (error) {
16762
+ sendHttpError(res, error);
16763
+ }
16764
+ return true;
16765
+ }
16766
+ const mcpRemoveMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/(?!(?:add|reconnect|stop|startup-metrics)$)([^/]+)$`).exec(url);
16767
+ if (mcpRemoveMatch && method === "DELETE") {
16768
+ try {
16769
+ await manager.removeMcpServer(decodeURIComponent(mcpRemoveMatch[1]), decodeURIComponent(mcpRemoveMatch[2]));
16770
+ sendJson(res, 200, { ok: true });
16771
+ } catch (error) {
16772
+ sendHttpError(res, error);
16773
+ }
16774
+ return true;
16775
+ }
16776
+ const tasksMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks$`).exec(url);
16777
+ if (tasksMatch && method === "GET") {
16778
+ try {
16779
+ const query = new URLSearchParams(url.split("?")[1] ?? "");
16780
+ const activeOnly = query.get("activeOnly") === "true" ? true : query.get("activeOnly") === "false" ? false : void 0;
16781
+ const limit = query.get("limit") ? Number(query.get("limit")) : void 0;
16782
+ sendJson(res, 200, await manager.listBackgroundTasks(decodeURIComponent(tasksMatch[1]), activeOnly, limit));
16783
+ } catch (error) {
16784
+ sendHttpError(res, error);
16785
+ }
16786
+ return true;
16787
+ }
16788
+ const taskOutputMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks/([^/]+)/output$`).exec(url);
16789
+ if (taskOutputMatch && method === "GET") {
16790
+ try {
16791
+ const query = new URLSearchParams(url.split("?")[1] ?? "");
16792
+ const tail = query.get("tail") ? Number(query.get("tail")) : void 0;
16793
+ sendJson(res, 200, { output: await manager.getBackgroundTaskOutput(decodeURIComponent(taskOutputMatch[1]), decodeURIComponent(taskOutputMatch[2]), tail) });
16794
+ } catch (error) {
16795
+ sendHttpError(res, error);
16796
+ }
16797
+ return true;
16798
+ }
16799
+ const taskStopMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks/([^/]+)/stop$`).exec(url);
16800
+ if (taskStopMatch && method === "POST") {
16801
+ try {
16802
+ const body = await readJsonBody(req);
16803
+ await manager.stopBackgroundTask(decodeURIComponent(taskStopMatch[1]), decodeURIComponent(taskStopMatch[2]), optionalString(body, "reason"));
16804
+ sendJson(res, 200, { ok: true });
16805
+ } catch (error) {
16806
+ sendHttpError(res, error);
16807
+ }
16808
+ return true;
16809
+ }
16810
+ return false;
16811
+ }
16812
+ async function handleGlobalRoutes(req, res, url, method, manager) {
16813
+ if (url === `${API_PREFIX}/config` && method === "GET") {
16814
+ try {
16815
+ sendJson(res, 200, await manager.getConfig());
16816
+ } catch (error) {
16817
+ sendHttpError(res, error);
16818
+ }
16819
+ return true;
16820
+ }
16821
+ if (url === `${API_PREFIX}/config` && method === "POST") {
16822
+ try {
16823
+ const patch = (await readJsonBody(req))["patch"];
16824
+ if (patch === null || typeof patch !== "object" || Array.isArray(patch)) throw new HttpError(400, "Missing or invalid patch field", "request.invalid");
16825
+ sendJson(res, 200, await manager.setConfig(patch));
16826
+ } catch (error) {
16827
+ sendHttpError(res, error);
16828
+ }
16829
+ return true;
16830
+ }
16831
+ const providerMatch = new RegExp(`^${API_PREFIX}/config/providers/([^/]+)$`).exec(url);
16832
+ if (providerMatch && method === "DELETE") {
16833
+ try {
16834
+ sendJson(res, 200, await manager.removeProvider(decodeURIComponent(providerMatch[1])));
16835
+ } catch (error) {
16836
+ sendHttpError(res, error);
16837
+ }
16838
+ return true;
16839
+ }
16840
+ if (url === `${API_PREFIX}/experimental-flags` && method === "GET") {
16841
+ try {
16842
+ sendJson(res, 200, await manager.getExperimentalFlags());
16843
+ } catch (error) {
16844
+ sendHttpError(res, error);
16845
+ }
16846
+ return true;
16847
+ }
16848
+ if (url === `${API_PREFIX}/preflight` && method === "GET") {
16849
+ try {
16850
+ await manager.preflight();
16851
+ sendJson(res, 200, { ok: true });
16852
+ } catch (error) {
16853
+ sendHttpError(res, error);
16854
+ }
16855
+ return true;
16856
+ }
16857
+ return false;
16858
+ }
16250
16859
  async function runWebServer(opts) {
16251
16860
  const homeDir = resolveScreamHome();
16252
16861
  const workDir = opts.workDir;
@@ -16356,6 +16965,9 @@ async function runWebServer(opts) {
16356
16965
  return;
16357
16966
  }
16358
16967
  if (await handleGoalRoute(req, res, url, (sessionId) => manager.get(sessionId))) return;
16968
+ if (await handleSessionControlRoutes(req, res, url, method, manager)) return;
16969
+ if (await handleResourceRoutes(req, res, url, method, manager)) return;
16970
+ if (await handleGlobalRoutes(req, res, url, method, manager)) return;
16359
16971
  const snapshotMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/snapshot(\\?|$)`).exec(url);
16360
16972
  if (snapshotMatch && method === "GET") {
16361
16973
  const sessionId = decodeURIComponent(snapshotMatch[1]);