scream-code 0.15.1 → 0.15.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.
@@ -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-B_ViYZer.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.2"),
36
36
  channel: optionalBuildString(""),
37
37
  commit: optionalBuildString(""),
38
38
  buildTarget: optionalBuildString("darwin-arm64")
@@ -12397,7 +12397,7 @@ var ScreamTUI = class {
12397
12397
  const card = new OnboardingCardComponent({
12398
12398
  colors: this.state.theme.colors,
12399
12399
  onConfigure: () => {
12400
- import("./dispatch-Doe-DlpL.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
12400
+ import("./dispatch-Dur5jHVw.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
12401
12401
  this.restoreEditor();
12402
12402
  onDone?.();
12403
12403
  });
@@ -14527,7 +14527,14 @@ function toHttpError(error) {
14527
14527
  if (isScreamError(error)) switch (error.code) {
14528
14528
  case ErrorCodes.GOAL_OBJECTIVE_EMPTY:
14529
14529
  case ErrorCodes.GOAL_OBJECTIVE_TOO_LONG:
14530
- case ErrorCodes.REQUEST_INVALID: return new HttpError(400, error.message, error.code);
14530
+ case ErrorCodes.REQUEST_INVALID:
14531
+ case ErrorCodes.SESSION_PERMISSION_MODE_INVALID:
14532
+ case ErrorCodes.SESSION_PLAN_MODE_INVALID:
14533
+ case ErrorCodes.SESSION_WOLFPACK_MODE_INVALID:
14534
+ case ErrorCodes.SESSION_MODEL_EMPTY:
14535
+ case ErrorCodes.SESSION_THINKING_EMPTY:
14536
+ case ErrorCodes.SKILL_NAME_EMPTY:
14537
+ case ErrorCodes.BACKGROUND_TASK_ID_EMPTY: return new HttpError(400, error.message, error.code);
14531
14538
  case ErrorCodes.GOAL_NOT_FOUND:
14532
14539
  case ErrorCodes.SESSION_NOT_FOUND: return new HttpError(404, error.message, error.code);
14533
14540
  case ErrorCodes.GOAL_ALREADY_EXISTS:
@@ -15696,6 +15703,49 @@ var WebSession = class {
15696
15703
  status: this.cachedStatus
15697
15704
  }, false);
15698
15705
  }
15706
+ /** Switch permission mode, then sync status + metadata (TUI /auto,/yes parity). */
15707
+ async switchPermission(mode) {
15708
+ if (!this.session) throw new HttpError(409, "会话已归档(只读),无法切换权限模式。");
15709
+ await this.session.setPermission(mode);
15710
+ this.permission = mode;
15711
+ await this.refreshStatus();
15712
+ this.broadcast({
15713
+ type: "status",
15714
+ status: this.cachedStatus
15715
+ }, false);
15716
+ if (this.homeDir) await saveMetadata(this.homeDir, this.getMetadata());
15717
+ }
15718
+ /** Toggle plan mode, then sync status (TUI /plan parity). */
15719
+ async switchPlanMode(enabled, strategy) {
15720
+ if (!this.session) throw new HttpError(409, "会话已归档(只读),无法切换计划模式。");
15721
+ await this.session.setPlanMode(enabled, strategy);
15722
+ await this.refreshStatus();
15723
+ this.broadcast({
15724
+ type: "status",
15725
+ status: this.cachedStatus
15726
+ }, false);
15727
+ }
15728
+ /** Toggle wolfpack mode, then sync status. */
15729
+ async switchWolfpackMode(enabled) {
15730
+ if (!this.session) throw new HttpError(409, "会话已归档(只读),无法切换 Wolfpack 模式。");
15731
+ await this.session.setWolfpackMode(enabled);
15732
+ await this.refreshStatus();
15733
+ this.broadcast({
15734
+ type: "status",
15735
+ status: this.cachedStatus
15736
+ }, false);
15737
+ }
15738
+ /** Toggle RLM persistent-python mode (+ optional max depth), then sync status. */
15739
+ async switchRlm(enabled, maxDepth) {
15740
+ if (!this.session) throw new HttpError(409, "会话已归档(只读),无法切换 RLM 模式。");
15741
+ await this.session.setRlmEnabled(enabled);
15742
+ if (maxDepth !== void 0) await this.session.setRlmMaxDepth(maxDepth);
15743
+ await this.refreshStatus();
15744
+ this.broadcast({
15745
+ type: "status",
15746
+ status: this.cachedStatus
15747
+ }, false);
15748
+ }
15699
15749
  getCoreSessionId() {
15700
15750
  return this.coreSessionId;
15701
15751
  }
@@ -15758,6 +15808,15 @@ var WebSession = class {
15758
15808
  await session.cancelGoal();
15759
15809
  }, true);
15760
15810
  }
15811
+ /**
15812
+ * Public handle to the live core Session for REST RPC forwarding.
15813
+ * Throws 409 for archived (read-only) sessions so handlers never call into a
15814
+ * closed core session instead of silently creating one.
15815
+ */
15816
+ requireLiveSession() {
15817
+ if (!this.session) throw new HttpError(409, "Session is archived (read-only)", ErrorCodes.SESSION_CLOSED);
15818
+ return this.session;
15819
+ }
15761
15820
  requireGoalSession(allowBusy = false) {
15762
15821
  if (!this.session) throw new HttpError(409, "Session is archived (read-only)", ErrorCodes.SESSION_CLOSED);
15763
15822
  if (!allowBusy && this.busy) throw new HttpError(409, "Session is busy", ErrorCodes.TURN_AGENT_BUSY);
@@ -16246,7 +16305,529 @@ var SessionManager = class {
16246
16305
  for (const [, ws] of this.sessions) await ws.close();
16247
16306
  this.sessions.clear();
16248
16307
  }
16308
+ /** Resolve a live session or throw 404/409; the single guard for all handlers. */
16309
+ getLiveSession(sessionId) {
16310
+ const ws = this.sessions.get(sessionId);
16311
+ if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
16312
+ return ws.requireLiveSession();
16313
+ }
16314
+ async getSessionStatus(sessionId) {
16315
+ return this.getLiveSession(sessionId).getStatus();
16316
+ }
16317
+ async getSessionUsage(sessionId) {
16318
+ return this.getLiveSession(sessionId).getUsage();
16319
+ }
16320
+ async getSessionContext(sessionId) {
16321
+ return this.getLiveSession(sessionId).getContext();
16322
+ }
16323
+ async getSessionPlan(sessionId) {
16324
+ return this.getLiveSession(sessionId).getPlan();
16325
+ }
16326
+ async setPermission(sessionId, mode) {
16327
+ const ws = this.sessions.get(sessionId);
16328
+ if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
16329
+ await ws.switchPermission(mode);
16330
+ }
16331
+ async setPlanMode(sessionId, enabled, strategy) {
16332
+ const ws = this.sessions.get(sessionId);
16333
+ if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
16334
+ await ws.switchPlanMode(enabled, strategy);
16335
+ }
16336
+ async clearPlan(sessionId) {
16337
+ await this.getLiveSession(sessionId).clearPlan();
16338
+ }
16339
+ async setWolfpackMode(sessionId, enabled) {
16340
+ const ws = this.sessions.get(sessionId);
16341
+ if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
16342
+ await ws.switchWolfpackMode(enabled);
16343
+ }
16344
+ async setRlm(sessionId, enabled, maxDepth) {
16345
+ const ws = this.sessions.get(sessionId);
16346
+ if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
16347
+ await ws.switchRlm(enabled, maxDepth);
16348
+ }
16349
+ async undoHistory(sessionId, count) {
16350
+ await this.getLiveSession(sessionId).undoHistory(count);
16351
+ }
16352
+ async compact(sessionId, instruction) {
16353
+ await this.getLiveSession(sessionId).compact(instruction ? { instruction } : {});
16354
+ }
16355
+ async listSkills(sessionId) {
16356
+ return this.getLiveSession(sessionId).listSkills();
16357
+ }
16358
+ async activateSkill(sessionId, name, args) {
16359
+ await this.getLiveSession(sessionId).activateSkill(name, args);
16360
+ }
16361
+ async removeSkill(sessionId, name) {
16362
+ await this.getLiveSession(sessionId).removeSkill(name);
16363
+ }
16364
+ async listPlugins(sessionId) {
16365
+ return this.getLiveSession(sessionId).listPlugins();
16366
+ }
16367
+ async getPluginInfo(sessionId, id) {
16368
+ return this.getLiveSession(sessionId).getPluginInfo(id);
16369
+ }
16370
+ async installPlugin(sessionId, source) {
16371
+ return this.getLiveSession(sessionId).installPlugin(source);
16372
+ }
16373
+ async setPluginEnabled(sessionId, id, enabled) {
16374
+ await this.getLiveSession(sessionId).setPluginEnabled(id, enabled);
16375
+ }
16376
+ async setPluginMcpServerEnabled(sessionId, id, server, enabled) {
16377
+ await this.getLiveSession(sessionId).setPluginMcpServerEnabled(id, server, enabled);
16378
+ }
16379
+ async removePlugin(sessionId, id) {
16380
+ await this.getLiveSession(sessionId).removePlugin(id);
16381
+ }
16382
+ async reloadPlugins(sessionId) {
16383
+ return this.getLiveSession(sessionId).reloadPlugins();
16384
+ }
16385
+ async activatePlugin(sessionId, id) {
16386
+ await this.getLiveSession(sessionId).activatePlugin(id);
16387
+ }
16388
+ async deactivatePlugin(sessionId, id) {
16389
+ await this.getLiveSession(sessionId).deactivatePlugin(id);
16390
+ }
16391
+ async injectPlugin(sessionId, id) {
16392
+ await this.getLiveSession(sessionId).injectPlugin(id);
16393
+ }
16394
+ async listMcpServers(sessionId) {
16395
+ return this.getLiveSession(sessionId).listMcpServers();
16396
+ }
16397
+ async getMcpStartupMetrics(sessionId) {
16398
+ return this.getLiveSession(sessionId).getMcpStartupMetrics();
16399
+ }
16400
+ async addMcpServer(sessionId, name, config) {
16401
+ await this.getLiveSession(sessionId).addMcpServer(name, config);
16402
+ }
16403
+ async reconnectMcpServer(sessionId, name) {
16404
+ await this.getLiveSession(sessionId).reconnectMcpServer(name);
16405
+ }
16406
+ async stopMcpServer(sessionId, name) {
16407
+ await this.getLiveSession(sessionId).stopMcpServer(name);
16408
+ }
16409
+ async removeMcpServer(sessionId, name) {
16410
+ await this.getLiveSession(sessionId).removeMcpServer(name);
16411
+ }
16412
+ async listBackgroundTasks(sessionId, activeOnly, limit) {
16413
+ return this.getLiveSession(sessionId).listBackgroundTasks({
16414
+ activeOnly,
16415
+ limit
16416
+ });
16417
+ }
16418
+ async getBackgroundTaskOutput(sessionId, taskId, tail) {
16419
+ return this.getLiveSession(sessionId).getBackgroundTaskOutput(taskId, tail !== void 0 ? { tail } : {});
16420
+ }
16421
+ async stopBackgroundTask(sessionId, taskId, reason) {
16422
+ await this.getLiveSession(sessionId).stopBackgroundTask(taskId, reason !== void 0 ? { reason } : {});
16423
+ }
16424
+ getConfig(options) {
16425
+ return this.harness.getConfig(options);
16426
+ }
16427
+ setConfig(patch) {
16428
+ return this.harness.setConfig(patch);
16429
+ }
16430
+ removeProvider(providerId) {
16431
+ return this.harness.removeProvider(providerId);
16432
+ }
16433
+ getExperimentalFlags() {
16434
+ return this.harness.getExperimentalFlags();
16435
+ }
16436
+ preflight() {
16437
+ return this.harness.preflight();
16438
+ }
16249
16439
  };
16440
+ async function handleSessionControlRoutes(req, res, url, method, manager) {
16441
+ const statusMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/status$`).exec(url);
16442
+ if (statusMatch && method === "GET") {
16443
+ try {
16444
+ sendJson(res, 200, await manager.getSessionStatus(decodeURIComponent(statusMatch[1])));
16445
+ } catch (error) {
16446
+ sendHttpError(res, error);
16447
+ }
16448
+ return true;
16449
+ }
16450
+ const usageMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/usage$`).exec(url);
16451
+ if (usageMatch && method === "GET") {
16452
+ try {
16453
+ sendJson(res, 200, await manager.getSessionUsage(decodeURIComponent(usageMatch[1])));
16454
+ } catch (error) {
16455
+ sendHttpError(res, error);
16456
+ }
16457
+ return true;
16458
+ }
16459
+ const contextMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/context$`).exec(url);
16460
+ if (contextMatch && method === "GET") {
16461
+ try {
16462
+ sendJson(res, 200, await manager.getSessionContext(decodeURIComponent(contextMatch[1])));
16463
+ } catch (error) {
16464
+ sendHttpError(res, error);
16465
+ }
16466
+ return true;
16467
+ }
16468
+ const planMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plan$`).exec(url);
16469
+ if (planMatch && method === "GET") {
16470
+ try {
16471
+ sendJson(res, 200, await manager.getSessionPlan(decodeURIComponent(planMatch[1])));
16472
+ } catch (error) {
16473
+ sendHttpError(res, error);
16474
+ }
16475
+ return true;
16476
+ }
16477
+ const permissionMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/permission$`).exec(url);
16478
+ if (permissionMatch && method === "POST") {
16479
+ try {
16480
+ const mode = requiredString(await readJsonBody(req), "mode");
16481
+ await manager.setPermission(decodeURIComponent(permissionMatch[1]), mode);
16482
+ sendJson(res, 200, { ok: true });
16483
+ } catch (error) {
16484
+ sendHttpError(res, error);
16485
+ }
16486
+ return true;
16487
+ }
16488
+ const planModeMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plan$`).exec(url);
16489
+ if (planModeMatch && method === "POST") {
16490
+ try {
16491
+ const body = await readJsonBody(req);
16492
+ const enabled = optionalBoolean(body, "enabled", true);
16493
+ const strategy = optionalString(body, "strategy");
16494
+ await manager.setPlanMode(decodeURIComponent(planModeMatch[1]), enabled, strategy);
16495
+ sendJson(res, 200, { ok: true });
16496
+ } catch (error) {
16497
+ sendHttpError(res, error);
16498
+ }
16499
+ return true;
16500
+ }
16501
+ const clearPlanMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plan/clear$`).exec(url);
16502
+ if (clearPlanMatch && method === "POST") {
16503
+ try {
16504
+ await manager.clearPlan(decodeURIComponent(clearPlanMatch[1]));
16505
+ sendJson(res, 200, { ok: true });
16506
+ } catch (error) {
16507
+ sendHttpError(res, error);
16508
+ }
16509
+ return true;
16510
+ }
16511
+ const wolfpackMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/wolfpack$`).exec(url);
16512
+ if (wolfpackMatch && method === "POST") {
16513
+ try {
16514
+ const enabled = optionalBoolean(await readJsonBody(req), "enabled", true);
16515
+ await manager.setWolfpackMode(decodeURIComponent(wolfpackMatch[1]), enabled);
16516
+ sendJson(res, 200, { ok: true });
16517
+ } catch (error) {
16518
+ sendHttpError(res, error);
16519
+ }
16520
+ return true;
16521
+ }
16522
+ const rlmMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/rlm$`).exec(url);
16523
+ if (rlmMatch && method === "POST") {
16524
+ try {
16525
+ const body = await readJsonBody(req);
16526
+ const enabled = optionalBoolean(body, "enabled", true);
16527
+ const maxDepth = typeof body["maxDepth"] === "number" ? Number(body["maxDepth"]) : void 0;
16528
+ await manager.setRlm(decodeURIComponent(rlmMatch[1]), enabled, maxDepth);
16529
+ sendJson(res, 200, { ok: true });
16530
+ } catch (error) {
16531
+ sendHttpError(res, error);
16532
+ }
16533
+ return true;
16534
+ }
16535
+ const undoMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/undo$`).exec(url);
16536
+ if (undoMatch && method === "POST") {
16537
+ try {
16538
+ const body = await readJsonBody(req);
16539
+ const count = typeof body["count"] === "number" ? Number(body["count"]) : 1;
16540
+ await manager.undoHistory(decodeURIComponent(undoMatch[1]), count);
16541
+ sendJson(res, 200, { ok: true });
16542
+ } catch (error) {
16543
+ sendHttpError(res, error);
16544
+ }
16545
+ return true;
16546
+ }
16547
+ const compactMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/compact$`).exec(url);
16548
+ if (compactMatch && method === "POST") {
16549
+ try {
16550
+ const body = await readJsonBody(req);
16551
+ await manager.compact(decodeURIComponent(compactMatch[1]), optionalString(body, "instruction"));
16552
+ sendJson(res, 200, { ok: true });
16553
+ } catch (error) {
16554
+ sendHttpError(res, error);
16555
+ }
16556
+ return true;
16557
+ }
16558
+ return false;
16559
+ }
16560
+ async function handleResourceRoutes(req, res, url, method, manager) {
16561
+ const skillsMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/skills$`).exec(url);
16562
+ if (skillsMatch && method === "GET") {
16563
+ try {
16564
+ sendJson(res, 200, await manager.listSkills(decodeURIComponent(skillsMatch[1])));
16565
+ } catch (error) {
16566
+ sendHttpError(res, error);
16567
+ }
16568
+ return true;
16569
+ }
16570
+ const skillActivateMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/skills/([^/]+)/activate$`).exec(url);
16571
+ if (skillActivateMatch && method === "POST") {
16572
+ try {
16573
+ const body = await readJsonBody(req);
16574
+ await manager.activateSkill(decodeURIComponent(skillActivateMatch[1]), decodeURIComponent(skillActivateMatch[2]), optionalString(body, "args"));
16575
+ sendJson(res, 200, { ok: true });
16576
+ } catch (error) {
16577
+ sendHttpError(res, error);
16578
+ }
16579
+ return true;
16580
+ }
16581
+ const skillRemoveMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/skills/([^/]+)$`).exec(url);
16582
+ if (skillRemoveMatch && method === "DELETE") {
16583
+ try {
16584
+ await manager.removeSkill(decodeURIComponent(skillRemoveMatch[1]), decodeURIComponent(skillRemoveMatch[2]));
16585
+ sendJson(res, 200, { ok: true });
16586
+ } catch (error) {
16587
+ sendHttpError(res, error);
16588
+ }
16589
+ return true;
16590
+ }
16591
+ const pluginsMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins$`).exec(url);
16592
+ if (pluginsMatch && method === "GET") {
16593
+ try {
16594
+ sendJson(res, 200, await manager.listPlugins(decodeURIComponent(pluginsMatch[1])));
16595
+ } catch (error) {
16596
+ sendHttpError(res, error);
16597
+ }
16598
+ return true;
16599
+ }
16600
+ const pluginInstallMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/install$`).exec(url);
16601
+ if (pluginInstallMatch && method === "POST") {
16602
+ try {
16603
+ const body = await readJsonBody(req);
16604
+ sendJson(res, 200, await manager.installPlugin(decodeURIComponent(pluginInstallMatch[1]), requiredString(body, "source")));
16605
+ } catch (error) {
16606
+ sendHttpError(res, error);
16607
+ }
16608
+ return true;
16609
+ }
16610
+ const pluginReloadMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/reload$`).exec(url);
16611
+ if (pluginReloadMatch && method === "POST") {
16612
+ try {
16613
+ sendJson(res, 200, await manager.reloadPlugins(decodeURIComponent(pluginReloadMatch[1])));
16614
+ } catch (error) {
16615
+ sendHttpError(res, error);
16616
+ }
16617
+ return true;
16618
+ }
16619
+ const pluginEnableMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/enable$`).exec(url);
16620
+ if (pluginEnableMatch && method === "POST") {
16621
+ try {
16622
+ const body = await readJsonBody(req);
16623
+ await manager.setPluginEnabled(decodeURIComponent(pluginEnableMatch[1]), decodeURIComponent(pluginEnableMatch[2]), optionalBoolean(body, "enabled", true));
16624
+ sendJson(res, 200, { ok: true });
16625
+ } catch (error) {
16626
+ sendHttpError(res, error);
16627
+ }
16628
+ return true;
16629
+ }
16630
+ const pluginMcpEnableMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/mcp/([^/]+)/enable$`).exec(url);
16631
+ if (pluginMcpEnableMatch && method === "POST") {
16632
+ try {
16633
+ const body = await readJsonBody(req);
16634
+ await manager.setPluginMcpServerEnabled(decodeURIComponent(pluginMcpEnableMatch[1]), decodeURIComponent(pluginMcpEnableMatch[2]), decodeURIComponent(pluginMcpEnableMatch[3]), optionalBoolean(body, "enabled", true));
16635
+ sendJson(res, 200, { ok: true });
16636
+ } catch (error) {
16637
+ sendHttpError(res, error);
16638
+ }
16639
+ return true;
16640
+ }
16641
+ const pluginActivateMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/activate$`).exec(url);
16642
+ if (pluginActivateMatch && method === "POST") {
16643
+ try {
16644
+ await manager.activatePlugin(decodeURIComponent(pluginActivateMatch[1]), decodeURIComponent(pluginActivateMatch[2]));
16645
+ sendJson(res, 200, { ok: true });
16646
+ } catch (error) {
16647
+ sendHttpError(res, error);
16648
+ }
16649
+ return true;
16650
+ }
16651
+ const pluginDeactivateMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/deactivate$`).exec(url);
16652
+ if (pluginDeactivateMatch && method === "POST") {
16653
+ try {
16654
+ await manager.deactivatePlugin(decodeURIComponent(pluginDeactivateMatch[1]), decodeURIComponent(pluginDeactivateMatch[2]));
16655
+ sendJson(res, 200, { ok: true });
16656
+ } catch (error) {
16657
+ sendHttpError(res, error);
16658
+ }
16659
+ return true;
16660
+ }
16661
+ const pluginInjectMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/inject$`).exec(url);
16662
+ if (pluginInjectMatch && method === "POST") {
16663
+ try {
16664
+ await manager.injectPlugin(decodeURIComponent(pluginInjectMatch[1]), decodeURIComponent(pluginInjectMatch[2]));
16665
+ sendJson(res, 200, { ok: true });
16666
+ } catch (error) {
16667
+ sendHttpError(res, error);
16668
+ }
16669
+ return true;
16670
+ }
16671
+ const pluginInfoMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/(?!(?:install|reload)$)([^/]+)$`).exec(url);
16672
+ if (pluginInfoMatch && method === "GET") {
16673
+ try {
16674
+ sendJson(res, 200, await manager.getPluginInfo(decodeURIComponent(pluginInfoMatch[1]), decodeURIComponent(pluginInfoMatch[2])));
16675
+ } catch (error) {
16676
+ sendHttpError(res, error);
16677
+ }
16678
+ return true;
16679
+ }
16680
+ if (pluginInfoMatch && method === "DELETE") {
16681
+ try {
16682
+ await manager.removePlugin(decodeURIComponent(pluginInfoMatch[1]), decodeURIComponent(pluginInfoMatch[2]));
16683
+ sendJson(res, 200, { ok: true });
16684
+ } catch (error) {
16685
+ sendHttpError(res, error);
16686
+ }
16687
+ return true;
16688
+ }
16689
+ const mcpMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp$`).exec(url);
16690
+ if (mcpMatch && method === "GET") {
16691
+ try {
16692
+ sendJson(res, 200, await manager.listMcpServers(decodeURIComponent(mcpMatch[1])));
16693
+ } catch (error) {
16694
+ sendHttpError(res, error);
16695
+ }
16696
+ return true;
16697
+ }
16698
+ const mcpMetricsMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/startup-metrics$`).exec(url);
16699
+ if (mcpMetricsMatch && method === "GET") {
16700
+ try {
16701
+ sendJson(res, 200, await manager.getMcpStartupMetrics(decodeURIComponent(mcpMetricsMatch[1])));
16702
+ } catch (error) {
16703
+ sendHttpError(res, error);
16704
+ }
16705
+ return true;
16706
+ }
16707
+ const mcpAddMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/add$`).exec(url);
16708
+ if (mcpAddMatch && method === "POST") {
16709
+ try {
16710
+ const body = await readJsonBody(req);
16711
+ await manager.addMcpServer(decodeURIComponent(mcpAddMatch[1]), requiredString(body, "name"), body["config"] ?? {});
16712
+ sendJson(res, 200, { ok: true });
16713
+ } catch (error) {
16714
+ sendHttpError(res, error);
16715
+ }
16716
+ return true;
16717
+ }
16718
+ const mcpReconnectMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/([^/]+)/reconnect$`).exec(url);
16719
+ if (mcpReconnectMatch && method === "POST") {
16720
+ try {
16721
+ await manager.reconnectMcpServer(decodeURIComponent(mcpReconnectMatch[1]), decodeURIComponent(mcpReconnectMatch[2]));
16722
+ sendJson(res, 200, { ok: true });
16723
+ } catch (error) {
16724
+ sendHttpError(res, error);
16725
+ }
16726
+ return true;
16727
+ }
16728
+ const mcpStopMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/([^/]+)/stop$`).exec(url);
16729
+ if (mcpStopMatch && method === "POST") {
16730
+ try {
16731
+ await manager.stopMcpServer(decodeURIComponent(mcpStopMatch[1]), decodeURIComponent(mcpStopMatch[2]));
16732
+ sendJson(res, 200, { ok: true });
16733
+ } catch (error) {
16734
+ sendHttpError(res, error);
16735
+ }
16736
+ return true;
16737
+ }
16738
+ const mcpRemoveMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/(?!(?:add|reconnect|stop|startup-metrics)$)([^/]+)$`).exec(url);
16739
+ if (mcpRemoveMatch && method === "DELETE") {
16740
+ try {
16741
+ await manager.removeMcpServer(decodeURIComponent(mcpRemoveMatch[1]), decodeURIComponent(mcpRemoveMatch[2]));
16742
+ sendJson(res, 200, { ok: true });
16743
+ } catch (error) {
16744
+ sendHttpError(res, error);
16745
+ }
16746
+ return true;
16747
+ }
16748
+ const tasksMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks$`).exec(url);
16749
+ if (tasksMatch && method === "GET") {
16750
+ try {
16751
+ const query = new URLSearchParams(url.split("?")[1] ?? "");
16752
+ const activeOnly = query.get("activeOnly") === "true" ? true : query.get("activeOnly") === "false" ? false : void 0;
16753
+ const limit = query.get("limit") ? Number(query.get("limit")) : void 0;
16754
+ sendJson(res, 200, await manager.listBackgroundTasks(decodeURIComponent(tasksMatch[1]), activeOnly, limit));
16755
+ } catch (error) {
16756
+ sendHttpError(res, error);
16757
+ }
16758
+ return true;
16759
+ }
16760
+ const taskOutputMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks/([^/]+)/output$`).exec(url);
16761
+ if (taskOutputMatch && method === "GET") {
16762
+ try {
16763
+ const query = new URLSearchParams(url.split("?")[1] ?? "");
16764
+ const tail = query.get("tail") ? Number(query.get("tail")) : void 0;
16765
+ sendJson(res, 200, { output: await manager.getBackgroundTaskOutput(decodeURIComponent(taskOutputMatch[1]), decodeURIComponent(taskOutputMatch[2]), tail) });
16766
+ } catch (error) {
16767
+ sendHttpError(res, error);
16768
+ }
16769
+ return true;
16770
+ }
16771
+ const taskStopMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks/([^/]+)/stop$`).exec(url);
16772
+ if (taskStopMatch && method === "POST") {
16773
+ try {
16774
+ const body = await readJsonBody(req);
16775
+ await manager.stopBackgroundTask(decodeURIComponent(taskStopMatch[1]), decodeURIComponent(taskStopMatch[2]), optionalString(body, "reason"));
16776
+ sendJson(res, 200, { ok: true });
16777
+ } catch (error) {
16778
+ sendHttpError(res, error);
16779
+ }
16780
+ return true;
16781
+ }
16782
+ return false;
16783
+ }
16784
+ async function handleGlobalRoutes(req, res, url, method, manager) {
16785
+ if (url === `${API_PREFIX}/config` && method === "GET") {
16786
+ try {
16787
+ sendJson(res, 200, await manager.getConfig());
16788
+ } catch (error) {
16789
+ sendHttpError(res, error);
16790
+ }
16791
+ return true;
16792
+ }
16793
+ if (url === `${API_PREFIX}/config` && method === "POST") {
16794
+ try {
16795
+ const patch = (await readJsonBody(req))["patch"];
16796
+ if (patch === null || typeof patch !== "object" || Array.isArray(patch)) throw new HttpError(400, "Missing or invalid patch field", "request.invalid");
16797
+ sendJson(res, 200, await manager.setConfig(patch));
16798
+ } catch (error) {
16799
+ sendHttpError(res, error);
16800
+ }
16801
+ return true;
16802
+ }
16803
+ const providerMatch = new RegExp(`^${API_PREFIX}/config/providers/([^/]+)$`).exec(url);
16804
+ if (providerMatch && method === "DELETE") {
16805
+ try {
16806
+ sendJson(res, 200, await manager.removeProvider(decodeURIComponent(providerMatch[1])));
16807
+ } catch (error) {
16808
+ sendHttpError(res, error);
16809
+ }
16810
+ return true;
16811
+ }
16812
+ if (url === `${API_PREFIX}/experimental-flags` && method === "GET") {
16813
+ try {
16814
+ sendJson(res, 200, await manager.getExperimentalFlags());
16815
+ } catch (error) {
16816
+ sendHttpError(res, error);
16817
+ }
16818
+ return true;
16819
+ }
16820
+ if (url === `${API_PREFIX}/preflight` && method === "GET") {
16821
+ try {
16822
+ await manager.preflight();
16823
+ sendJson(res, 200, { ok: true });
16824
+ } catch (error) {
16825
+ sendHttpError(res, error);
16826
+ }
16827
+ return true;
16828
+ }
16829
+ return false;
16830
+ }
16250
16831
  async function runWebServer(opts) {
16251
16832
  const homeDir = resolveScreamHome();
16252
16833
  const workDir = opts.workDir;
@@ -16356,6 +16937,9 @@ async function runWebServer(opts) {
16356
16937
  return;
16357
16938
  }
16358
16939
  if (await handleGoalRoute(req, res, url, (sessionId) => manager.get(sessionId))) return;
16940
+ if (await handleSessionControlRoutes(req, res, url, method, manager)) return;
16941
+ if (await handleResourceRoutes(req, res, url, method, manager)) return;
16942
+ if (await handleGlobalRoutes(req, res, url, method, manager)) return;
16359
16943
  const snapshotMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/snapshot(\\?|$)`).exec(url);
16360
16944
  if (snapshotMatch && method === "GET") {
16361
16945
  const sessionId = decodeURIComponent(snapshotMatch[1]);