scream-code 0.16.6 → 0.16.7
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 +9 -3
- package/dist/{app-D9oJ-Y8W.mjs → app-DDepBvBx.mjs} +246 -78
- package/dist/{dispatch-dDbUBD7J.mjs → dispatch-CIGyjBDa.mjs} +1 -1
- package/dist/{dispatch-fjwbqaYI.mjs → dispatch-Vw9ccFm7.mjs} +1593 -157
- package/dist/main.mjs +1 -1
- package/dist/public/assets/index-DMGV8LA-.js +240 -0
- package/dist/public/assets/index-DMGV8LA-.js.map +1 -0
- package/dist/public/assets/index-DdPmkwIL.css +1 -0
- package/dist/public/index.html +2 -2
- package/dist/{text-input-dialog-Cj0egbsU.mjs → text-input-dialog-B-6Pgo6m.mjs} +1 -1
- package/dist/{text-input-dialog-DBlx8pMR.mjs → text-input-dialog-D9ByqSsK.mjs} +134 -4
- package/package.json +3 -2
- package/dist/public/assets/index-DQBhBKK1.css +0 -1
- package/dist/public/assets/index-DakPeNBt.js +0 -240
- package/dist/public/assets/index-DakPeNBt.js.map +0 -1
|
@@ -4,8 +4,8 @@ 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 { $n as
|
|
8
|
-
import { a as setLocale, i as getLocale, o as t } from "./text-input-dialog-
|
|
7
|
+
import { $n as normalizeWorkDir, $t as lerpGradient, A as highlightLines, An as getKnowledgeStore, At as isTurnElapsedEnabled, B as ActivityGroupComponent, Bn as getDataDir, Bt as ENABLE_TERMINAL_THEME_REPORTING, Cn as getNoActiveSessionMessage, Ct as clearInfoPanelState, D as ToolCallComponent, Dn as isExperimentalFlagEnabled, Dt as createEditorTheme, E as UserMessageComponent, En as sortSlashCommands, Et as resolveThemeSync, F as StatusMessageComponent, Fn as TuiConfigParseError, Ft as detectTerminalTheme, G as AgentGroupComponent, Gn as CLI_UI_MODE, Gt as QUERY_TERMINAL_THEME, H as estimateTokens, Hn as getLogDir, Ht as OSC11_RESPONSE, I as replaceTabs, In as TuiLikePreferencesSchema, It as parseOsc11BackgroundTheme, J as getBreathingFrame, Jn as DEFAULT_CATALOG_URL, Jt as TERMINAL_THEME_DARK, K as WelcomeComponent, Kn as CLI_USER_AGENT_PRODUCT, Kt as TERMINAL_FOCUS_IN, L as sanitizeShellOutput, Ln as loadTuiConfig, Lt as DISABLE_TERMINAL_FOCUS_REPORTING, M as CachedContainer, Mn as startManualEmbeddingDownload, Mt as createThemeStyles, N as ThinkingComponent, Nn as PIXEL_PULSE_FRAMES, Nt as contrastTextHex, O as renderDiffLines, On as setExperimentalFlags, Ot as createMarkdownTheme, P as NoticeMessageComponent, Pn as PULSE_WAVE_FRAMES, Pt as getColorPalette, Q as refineGoal, Qn as encodeWorkDirKey, Qt as FooterComponent, R as SkillActivationComponent, Rn as saveTuiConfig, Rt as DISABLE_TERMINAL_THEME_REPORTING, S as readJsonlFile, Sn as getLlmNotSetMessage, St as supportsBalance, Tn as BUILTIN_SLASH_COMMANDS, U as getSharedSpeedTracker, Un as detectInstallSource, Ut as OSC11_RESPONSE_PREFIX, V as charsForTokenBudget, Vn as getInputHistoryFile, Vt as OSC11_QUERY, W as BackgroundAgentStatusComponent, Wn as CLI_COMMAND_NAME, Wt as OSC11_RESPONSE_PREFIX_NO_ESC, X as CompactionComponent, Xn as saveCatalogCache, Xt as isBusy, Y as resetBreathingClock, Yn as fetchCatalog, Yt as TERMINAL_THEME_LIGHT, Z as clearGoalState, Zn as ScreamHarness, Zt as isStreaming, _n as MAIN_AGENT_ID, a as syncDiagramCapabilityPrompt, an as printableChar, ar as MemoryMemoStore, at as toTerminalHyperlink, b as readUpdateCache, bn as getCtrlCHint, c as disposeChildren, cn as argsRecord, cr as resolveGlobalLogPath, d as isPlanExpandable, dn as parseStreamingArgs, dr as ErrorCodes, en as SubagentSlots, er as appendSessionIndexEntry, f as MoonLoader, fn as serializeToolResultOutput, fr as SCREAM_ERROR_INFO, ft as handleFusionPlanCommand, gn as EXIT_CONFIRM_WINDOW_MS, hn as EMPTY_SESSION_HINT_URL, i as buildRoleAdditionalText, ir as resolveScreamHome, it as isInternalMessage, j as langFromPath, jn as hasEmbeddingModelCache, jt as toggleEmptySessionHint, k as renderDiffLinesClustered, kn as embeddingFailureDetail, kt as isEmptySessionHintDismissed, l as hasDispose, ln as formatErrorMessage, lr as isScreamError, mn as truncateErrorMessage, mt as handlePlanCommand, n as clearEvalPanelState, nr as removeSessionIndexEntry, on as STATUS_BULLET, or as flushDiagnosticLogs, ot as changeThinkingLevel, p as formatMemoryMemoForInjection, pn as stringValue, q as BREATHE_CYCLE_MS, qn as PRODUCT_NAME, qt as TERMINAL_FOCUS_OUT, r as openUrl, sn as appendStreamingArgsPreview, sr as log, st as getModelCycleLevel, t as dispatchInput, tr as readSessionIndex, u as isExpandable, un as isTodoItemShape, ur as isOrphanedToolCallError, v as selectUpdateTarget, vn as SESSION_TIPS, w as getDaemonInstructions, wn as buildSkillSlashCommands, x as appendJsonlLine, xn as getCtrlDHint, xt as refreshProviderBalance, y as refreshUpdateCache, yn as TIP_ROTATION_INTERVAL_MS, z as AssistantMessageComponent, zn as detectShellEnvironment, zt as ENABLE_TERMINAL_FOCUS_REPORTING } from "./dispatch-Vw9ccFm7.mjs";
|
|
8
|
+
import { a as setLocale, i as getLocale, o as t } from "./text-input-dialog-D9ByqSsK.mjs";
|
|
9
9
|
import { createRequire } from "node:module";
|
|
10
10
|
import { randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
|
|
11
11
|
import { access, appendFile, chmod, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
@@ -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.16.
|
|
35
|
+
version: optionalBuildString("0.16.7"),
|
|
36
36
|
channel: optionalBuildString(""),
|
|
37
37
|
commit: optionalBuildString(""),
|
|
38
38
|
buildTarget: optionalBuildString("darwin-arm64")
|
|
@@ -2363,15 +2363,15 @@ function formatStepDebugTiming(input) {
|
|
|
2363
2363
|
const latency = input.llmFirstTokenLatencyMs;
|
|
2364
2364
|
const streamMs = input.llmStreamDurationMs;
|
|
2365
2365
|
if (latency === void 0 || streamMs === void 0) return void 0;
|
|
2366
|
-
const parts = [`TTFT: ${formatDuration
|
|
2366
|
+
const parts = [`TTFT: ${formatDuration(latency)}`];
|
|
2367
2367
|
const outputTokens = input.usage?.output;
|
|
2368
2368
|
if (outputTokens !== void 0 && outputTokens > 0 && streamMs > 0) {
|
|
2369
2369
|
const tps = (outputTokens / (streamMs / 1e3)).toFixed(1);
|
|
2370
|
-
parts.push(`TPS: ${tps} tok/s (${outputTokens} tokens in ${formatDuration
|
|
2370
|
+
parts.push(`TPS: ${tps} tok/s (${outputTokens} tokens in ${formatDuration(streamMs)})`);
|
|
2371
2371
|
}
|
|
2372
2372
|
return `[Debug] ${parts.join(" | ")}`;
|
|
2373
2373
|
}
|
|
2374
|
-
function formatDuration
|
|
2374
|
+
function formatDuration(ms) {
|
|
2375
2375
|
if (ms < 1e3) return `${Math.round(ms)}ms`;
|
|
2376
2376
|
return `${(ms / 1e3).toFixed(1)}s`;
|
|
2377
2377
|
}
|
|
@@ -4907,6 +4907,7 @@ var StreamingUIController = class StreamingUIController {
|
|
|
4907
4907
|
this._assistantDraft = "";
|
|
4908
4908
|
this._shownAssistantLength = 0;
|
|
4909
4909
|
this.clearFlushTimerIfIdle();
|
|
4910
|
+
this._streamingBlock?.component.setStreaming(false);
|
|
4910
4911
|
this._streamingBlock = null;
|
|
4911
4912
|
this._thinkingDraft = "";
|
|
4912
4913
|
this.lastDeltaAt = void 0;
|
|
@@ -5010,6 +5011,7 @@ var StreamingUIController = class StreamingUIController {
|
|
|
5010
5011
|
const block = this._streamingBlock;
|
|
5011
5012
|
if (block !== null) {
|
|
5012
5013
|
block.entry.content = fullText;
|
|
5014
|
+
block.component.setStreaming(true);
|
|
5013
5015
|
block.component.updateContent(fullText);
|
|
5014
5016
|
this.host.state.ui.requestRender();
|
|
5015
5017
|
}
|
|
@@ -5019,6 +5021,7 @@ var StreamingUIController = class StreamingUIController {
|
|
|
5019
5021
|
const block = this._streamingBlock;
|
|
5020
5022
|
this._streamingBlock = null;
|
|
5021
5023
|
if (block !== null) {
|
|
5024
|
+
block.component.setStreaming(false);
|
|
5022
5025
|
this.host.transcriptController.unmarkPending(block.component);
|
|
5023
5026
|
this.host.transcriptController.commit();
|
|
5024
5027
|
}
|
|
@@ -6879,20 +6882,6 @@ const gitPanel = {
|
|
|
6879
6882
|
};
|
|
6880
6883
|
//#endregion
|
|
6881
6884
|
//#region src/tui/components/sidebar/panels/session-panel.ts
|
|
6882
|
-
function formatDuration(ms) {
|
|
6883
|
-
const totalSeconds = Math.max(0, Math.floor(ms / 1e3));
|
|
6884
|
-
if (getLocale() === "zh") {
|
|
6885
|
-
if (totalSeconds < 60) return `${totalSeconds}秒`;
|
|
6886
|
-
const hours = Math.floor(totalSeconds / 3600);
|
|
6887
|
-
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
6888
|
-
return hours > 0 ? `${hours}小时${minutes}分` : `${minutes}分钟`;
|
|
6889
|
-
}
|
|
6890
|
-
if (totalSeconds < 60) return `${totalSeconds}s`;
|
|
6891
|
-
const hours = Math.floor(totalSeconds / 3600);
|
|
6892
|
-
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
6893
|
-
if (hours > 0) return `${hours}h ${minutes}m`;
|
|
6894
|
-
return `${minutes}m`;
|
|
6895
|
-
}
|
|
6896
6885
|
/** 1,234 → "1,234" via the platform locale grouping. */
|
|
6897
6886
|
function formatCount$1(n) {
|
|
6898
6887
|
return n.toLocaleString("en-US");
|
|
@@ -6918,8 +6907,8 @@ function gridFits(rows, width) {
|
|
|
6918
6907
|
/**
|
|
6919
6908
|
* Session summary, live-rendered each frame as a strict two-column table:
|
|
6920
6909
|
* one field per row, labels sharing a left column, values sharing a right
|
|
6921
|
-
* edge.
|
|
6922
|
-
*
|
|
6910
|
+
* edge. Every value comes from the snapshot in getData(); nothing here is
|
|
6911
|
+
* derived from wall-clock time.
|
|
6923
6912
|
*/
|
|
6924
6913
|
var SessionPanelContent = class {
|
|
6925
6914
|
ctx;
|
|
@@ -6934,7 +6923,6 @@ var SessionPanelContent = class {
|
|
|
6934
6923
|
if (stats === void 0) return [chalk.hex(this.colors.textDim)(t("sidebar.session_empty"))];
|
|
6935
6924
|
const label = (s) => chalk.hex(this.colors.textDim)(s);
|
|
6936
6925
|
const value = (s) => chalk.hex(this.colors.text)(s);
|
|
6937
|
-
const now = Date.now();
|
|
6938
6926
|
const tokenRows = [
|
|
6939
6927
|
[t("sidebar.tokens"), stats.tokensTotal],
|
|
6940
6928
|
[t("sidebar.tokens_cache_hit"), stats.tokensInputCacheHit],
|
|
@@ -6942,7 +6930,6 @@ var SessionPanelContent = class {
|
|
|
6942
6930
|
[t("sidebar.tokens_output"), stats.tokensOutput]
|
|
6943
6931
|
];
|
|
6944
6932
|
const countRows = [
|
|
6945
|
-
[t("sidebar.uptime"), formatDuration(now - stats.startedAt)],
|
|
6946
6933
|
[t("sidebar.turns"), formatCount$1(stats.turns)],
|
|
6947
6934
|
[t("sidebar.tools"), formatCount$1(stats.toolCalls)],
|
|
6948
6935
|
[t("sidebar.calls"), formatCount$1(stats.apiCalls)],
|
|
@@ -6989,8 +6976,8 @@ function statusText(status) {
|
|
|
6989
6976
|
}
|
|
6990
6977
|
}
|
|
6991
6978
|
/**
|
|
6992
|
-
* Fixed
|
|
6993
|
-
* custom types appended in spawn order). Live-rendered each frame from the
|
|
6979
|
+
* Fixed default-slot subagent panel: one row per default subagent type (plus
|
|
6980
|
+
* any custom types appended in spawn order). Live-rendered each frame from the
|
|
6994
6981
|
* current snapshot; idle slots stay greyed out.
|
|
6995
6982
|
*/
|
|
6996
6983
|
var AgentsPanelContent = class {
|
|
@@ -7013,7 +7000,7 @@ var AgentsPanelContent = class {
|
|
|
7013
7000
|
renderSlot(cell, slot, index, width) {
|
|
7014
7001
|
const dim = (s) => chalk.hex(this.colors.textDim)(s);
|
|
7015
7002
|
const idle = slot.status === "idle";
|
|
7016
|
-
const phase = (Date.now() % AGENT_GRADIENT_CYCLE_MS / AGENT_GRADIENT_CYCLE_MS + index * .
|
|
7003
|
+
const phase = (Date.now() % AGENT_GRADIENT_CYCLE_MS / AGENT_GRADIENT_CYCLE_MS + index * .6180339887498949) % 1;
|
|
7017
7004
|
const active = slot.status === "working" || slot.status === "outputting" ? chalk.hex(lerpGradient(phase)).bold : void 0;
|
|
7018
7005
|
const marker = padLabel((active ?? chalk.hex(this.statusColor(slot.status)))(idle ? "○" : "●"), 2);
|
|
7019
7006
|
const label = (idle ? dim : chalk.hex(this.colors.textStrong))(cell.label);
|
|
@@ -7273,23 +7260,38 @@ function toneFor(site, stale) {
|
|
|
7273
7260
|
/**
|
|
7274
7261
|
* Merge the measured provider reading with the probe samples into Hub rows.
|
|
7275
7262
|
* The provider row always leads (it answers "is the model I'm using reachable"),
|
|
7276
|
-
*
|
|
7277
|
-
*
|
|
7263
|
+
* it staleness-checks against its own budget: a long idle stretch means nothing
|
|
7264
|
+
* was asked, not that the network died — and it is coloured on its own ladder
|
|
7265
|
+
* (see {@link hubModelTone}), not on the probe bar.
|
|
7278
7266
|
*/
|
|
7279
7267
|
function buildHubSamples(snapshot, provider, now = Date.now()) {
|
|
7280
7268
|
const ageMs = provider.sampledAt === void 0 ? 0 : now - provider.sampledAt;
|
|
7281
7269
|
return [{
|
|
7282
7270
|
id: HUB_MODEL_ROW_ID,
|
|
7283
7271
|
ms: provider.ms,
|
|
7284
|
-
tone:
|
|
7272
|
+
tone: hubModelTone(provider.ms, ageMs >= HUB_MODEL_STALE_MS)
|
|
7285
7273
|
}, ...snapshot.samples];
|
|
7286
7274
|
}
|
|
7287
|
-
/**
|
|
7275
|
+
/** Tone rule for the probed endpoint rows: green ≤{@link HUB_GOOD_MS}, amber above. */
|
|
7288
7276
|
function hubLatencyTone(ms, stale = false) {
|
|
7289
7277
|
if (ms === void 0 || stale) return "dim";
|
|
7290
7278
|
return ms <= 100 ? "ok" : "warn";
|
|
7291
7279
|
}
|
|
7292
7280
|
/**
|
|
7281
|
+
* Tone rule for the measured provider row (model first token), deliberately
|
|
7282
|
+
* separate from the probe ladder: a first token and a HEAD request are not
|
|
7283
|
+
* comparable, and one shared bar would call every healthy answer slow. Green
|
|
7284
|
+
* ≤{@link HUB_MODEL_GOOD_MS}, amber up to {@link HUB_MODEL_SLOW_MS}, red past
|
|
7285
|
+
* it — that long is worth flagging, not merely dimming. `undefined` (nothing
|
|
7286
|
+
* measured yet, or a provider just switched) and a stale reading stay dim:
|
|
7287
|
+
* neither is an outage.
|
|
7288
|
+
*/
|
|
7289
|
+
function hubModelTone(ms, stale = false) {
|
|
7290
|
+
if (ms === void 0 || stale) return "dim";
|
|
7291
|
+
if (ms <= 5e3) return "ok";
|
|
7292
|
+
return ms <= 15e3 ? "warn" : "down";
|
|
7293
|
+
}
|
|
7294
|
+
/**
|
|
7293
7295
|
* Compact latency reading: whole milliseconds below a second, one-decimal
|
|
7294
7296
|
* seconds above — a probe that never answers must never read as `65400ms`.
|
|
7295
7297
|
*/
|
|
@@ -7310,9 +7312,11 @@ const EMPTY_MARK = "--";
|
|
|
7310
7312
|
*
|
|
7311
7313
|
* Values share one right edge through the same two-column engine the Session
|
|
7312
7314
|
* panel uses, so rows never jitter as numbers change width, and only the value
|
|
7313
|
-
* carries colour
|
|
7314
|
-
*
|
|
7315
|
-
*
|
|
7315
|
+
* carries colour. Two ladders, because two different things are timed: probe
|
|
7316
|
+
* rows are green ≤100ms, amber above it, red once a site stopped answering,
|
|
7317
|
+
* while the measured provider row (model first token) is green ≤5s, amber up to
|
|
7318
|
+
* 15s and red past that. The panel is a pure view: probing happens in the data
|
|
7319
|
+
* provider, which is what keeps a slow endpoint from ever touching a frame.
|
|
7316
7320
|
*/
|
|
7317
7321
|
var HubPanelContent = class {
|
|
7318
7322
|
ctx;
|
|
@@ -7924,10 +7928,6 @@ var LifecycleController = class LifecycleController {
|
|
|
7924
7928
|
* frame asked, so a closed sidebar produces no traffic. */
|
|
7925
7929
|
hubProbe = createHubProbe();
|
|
7926
7930
|
sidebarGitCacheWorkDir = "";
|
|
7927
|
-
/** Session start time for the sidebar uptime counter. Reset on session
|
|
7928
|
-
* switch so a resumed old session doesn't show the TUI process age. */
|
|
7929
|
-
sidebarSessionStartedAt = Date.now();
|
|
7930
|
-
sidebarSessionId;
|
|
7931
7931
|
/** Memo for the session-stats aggregation: recomputed only when the
|
|
7932
7932
|
* transcript length, compaction count or usage object changes. */
|
|
7933
7933
|
sidebarStatsMemo;
|
|
@@ -8195,10 +8195,6 @@ var LifecycleController = class LifecycleController {
|
|
|
8195
8195
|
};
|
|
8196
8196
|
}
|
|
8197
8197
|
readSidebarSessionStats(appState) {
|
|
8198
|
-
if (this.sidebarSessionId !== appState.sessionId) {
|
|
8199
|
-
this.sidebarSessionId = appState.sessionId;
|
|
8200
|
-
this.sidebarSessionStartedAt = Date.now();
|
|
8201
|
-
}
|
|
8202
8198
|
const entries = this.host.state.transcriptEntries;
|
|
8203
8199
|
const usage = appState.sessionUsage;
|
|
8204
8200
|
const subagentUsage = appState.subagentUsage;
|
|
@@ -8220,8 +8216,7 @@ var LifecycleController = class LifecycleController {
|
|
|
8220
8216
|
tokensTotal: LifecycleController.sumTokens(usage) + subagentOutput,
|
|
8221
8217
|
tokensInputCacheHit: usage.inputCacheRead,
|
|
8222
8218
|
tokensInputCacheMiss: usage.inputOther + usage.inputCacheCreation,
|
|
8223
|
-
tokensOutput: usage.output + subagentOutput
|
|
8224
|
-
startedAt: this.sidebarSessionStartedAt
|
|
8219
|
+
tokensOutput: usage.output + subagentOutput
|
|
8225
8220
|
};
|
|
8226
8221
|
this.sidebarStatsMemo = {
|
|
8227
8222
|
length: entries.length,
|
|
@@ -8515,7 +8510,7 @@ var FileMentionProvider = class {
|
|
|
8515
8510
|
const atPrefix = extractAtPrefix(textBeforeCursor);
|
|
8516
8511
|
if (!options.force && atPrefix === null && textBeforeCursor.startsWith("/") && !textBeforeCursor.includes(" ")) {
|
|
8517
8512
|
const query = textBeforeCursor.slice(1);
|
|
8518
|
-
const filtered = fuzzyFilter(this.slashCommandItems, query, (item) => item.value).slice(
|
|
8513
|
+
const filtered = fuzzyFilter(this.slashCommandItems, query, (item) => !query.startsWith("skill:") && item.value.startsWith("skill:") ? item.value.slice(6) : item.value);
|
|
8519
8514
|
if (filtered.length === 0) return null;
|
|
8520
8515
|
return {
|
|
8521
8516
|
items: filtered,
|
|
@@ -9467,7 +9462,7 @@ const SIDE_PADDING = 1;
|
|
|
9467
9462
|
* Renders the stacked sidebar: every visible registered panel gets its own
|
|
9468
9463
|
* full box frame (┌─┐│└┘) with the panel title embedded in the top frame, one
|
|
9469
9464
|
* below the other. Panels render live each frame (their body reads the
|
|
9470
|
-
* current data snapshot), so time-based values (
|
|
9465
|
+
* current data snapshot), so time-based values (goal wall-clock)
|
|
9471
9466
|
* tick without rebuilding. The focused panel's title is bolded; focus is
|
|
9472
9467
|
* moved by the SidebarManager (`/sidebar next/prev`, Ctrl+X toggles).
|
|
9473
9468
|
* The whole frame is painted in the theme accent colour and the rendered
|
|
@@ -9481,9 +9476,10 @@ var SidebarContainer = class extends Container {
|
|
|
9481
9476
|
* only rebuilt when the visible panel set changes. */
|
|
9482
9477
|
bodies = /* @__PURE__ */ new Map();
|
|
9483
9478
|
builtStackKey = "";
|
|
9484
|
-
/** 1s heartbeat
|
|
9485
|
-
*
|
|
9486
|
-
*
|
|
9479
|
+
/** 1s heartbeat for the whole container lifetime: keeps live values (goal
|
|
9480
|
+
* wall-clock, gradient phase of busy agent slots) ticking while the sidebar
|
|
9481
|
+
* is open — the tick is a no-op when it is closed, and only `dispose()`
|
|
9482
|
+
* (TUI teardown) clears the interval. */
|
|
9487
9483
|
heartbeat;
|
|
9488
9484
|
constructor(manager, requestRender, colors) {
|
|
9489
9485
|
super();
|
|
@@ -11150,6 +11146,7 @@ var SessionManager$1 = class {
|
|
|
11150
11146
|
await this.unloadCurrentSession("switching session")?.close({ extractMemories: false });
|
|
11151
11147
|
this.host.session = session;
|
|
11152
11148
|
this.registerSessionHandlers(session);
|
|
11149
|
+
await syncDiagramCapabilityPrompt(session);
|
|
11153
11150
|
}
|
|
11154
11151
|
async syncRuntimeState(session = this.requireSession()) {
|
|
11155
11152
|
const status = await session.getStatus();
|
|
@@ -14056,7 +14053,7 @@ var ScreamTUI = class {
|
|
|
14056
14053
|
const card = new OnboardingCardComponent({
|
|
14057
14054
|
colors: this.state.theme.colors,
|
|
14058
14055
|
onConfigure: () => {
|
|
14059
|
-
import("./dispatch-
|
|
14056
|
+
import("./dispatch-CIGyjBDa.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
|
|
14060
14057
|
this.restoreEditor();
|
|
14061
14058
|
onDone?.();
|
|
14062
14059
|
});
|
|
@@ -16339,13 +16336,16 @@ async function ensureSessionsDir(homeDir) {
|
|
|
16339
16336
|
const dir = getSessionsDir(homeDir);
|
|
16340
16337
|
if (!existsSync(dir)) await mkdir(dir, { recursive: true });
|
|
16341
16338
|
}
|
|
16342
|
-
async function appendJournalLine(homeDir, sessionId, line) {
|
|
16339
|
+
async function appendJournalLine(homeDir, sessionId, line, throwOnError = false) {
|
|
16340
|
+
let writeError;
|
|
16343
16341
|
await appendJournalLineSerialized(getJournalPath(homeDir, sessionId), line, (error) => {
|
|
16342
|
+
writeError = error;
|
|
16344
16343
|
log.warn("web: failed to persist journal line", {
|
|
16345
16344
|
sessionId,
|
|
16346
16345
|
error: String(error)
|
|
16347
16346
|
});
|
|
16348
16347
|
});
|
|
16348
|
+
if (throwOnError && writeError !== void 0) throw writeError;
|
|
16349
16349
|
}
|
|
16350
16350
|
let metaWriteSeq = 0;
|
|
16351
16351
|
async function saveMetadata(homeDir, meta) {
|
|
@@ -16430,16 +16430,22 @@ function isMessageBearingEntry(entry) {
|
|
|
16430
16430
|
*/
|
|
16431
16431
|
function contextHistoryToChatMessages(history) {
|
|
16432
16432
|
const out = [];
|
|
16433
|
+
let pendingAnchor = "other";
|
|
16433
16434
|
for (const message of history) {
|
|
16434
16435
|
if (message.role !== "user" && message.role !== "assistant") continue;
|
|
16435
16436
|
if (isInternalMessage(message)) continue;
|
|
16436
|
-
|
|
16437
|
+
const anchor = classifyUndoAnchor(message.origin);
|
|
16438
|
+
if (message.role === "user") {
|
|
16439
|
+
pendingAnchor = anchor;
|
|
16440
|
+
if (anchor === "skill" || anchor === "other") continue;
|
|
16441
|
+
}
|
|
16437
16442
|
const text = message.content.map((part) => part.type === "text" ? part.text : "").join("").trim();
|
|
16438
16443
|
if (text.length === 0) continue;
|
|
16439
16444
|
out.push({
|
|
16440
16445
|
role: message.role,
|
|
16441
16446
|
content: text,
|
|
16442
|
-
tools: []
|
|
16447
|
+
tools: [],
|
|
16448
|
+
undoAnchor: message.role === "assistant" ? pendingAnchor : anchor
|
|
16443
16449
|
});
|
|
16444
16450
|
}
|
|
16445
16451
|
return out;
|
|
@@ -16469,6 +16475,65 @@ function deriveTitle(messages) {
|
|
|
16469
16475
|
const text = firstUser.content.trim();
|
|
16470
16476
|
return text.length > 40 ? text.slice(0, 40) + "..." : text;
|
|
16471
16477
|
}
|
|
16478
|
+
/**
|
|
16479
|
+
* Remove complete user-turn blocks from a rendered transcript. The core
|
|
16480
|
+
* context owns the authoritative undo operation; the web journal is append
|
|
16481
|
+
* only, so a durable undo marker asks this projection to hide the same turns
|
|
16482
|
+
* when snapshots are rebuilt (including after a restart).
|
|
16483
|
+
*
|
|
16484
|
+
* `beforeSeq` is an inclusive cursor captured before the core mutation. It
|
|
16485
|
+
* also covers a user message that reserved the next sequence but never
|
|
16486
|
+
* emitted a turn event.
|
|
16487
|
+
*/
|
|
16488
|
+
function removeLastTurnsBefore(messages, count, beforeSeq) {
|
|
16489
|
+
if (count <= 0 || messages.length === 0) return;
|
|
16490
|
+
const firstFutureIndex = messages.findIndex((message) => typeof message.seq === "number" && message.seq > beforeSeq);
|
|
16491
|
+
const prefixLength = firstFutureIndex === -1 ? messages.length : firstFutureIndex;
|
|
16492
|
+
const prefix = messages.slice(0, prefixLength);
|
|
16493
|
+
const tail = messages.slice(prefixLength);
|
|
16494
|
+
let remaining = count;
|
|
16495
|
+
while (remaining > 0) {
|
|
16496
|
+
let anchor = -1;
|
|
16497
|
+
for (let i = prefix.length - 1; i >= 0; i -= 1) {
|
|
16498
|
+
const message = prefix[i];
|
|
16499
|
+
if (message?.role === "assistant" && message.undoAnchor === "skill") {
|
|
16500
|
+
anchor = i;
|
|
16501
|
+
while (anchor > 0) {
|
|
16502
|
+
const previous = prefix[anchor - 1];
|
|
16503
|
+
if (previous?.role !== "assistant" || previous.undoAnchor !== "skill") break;
|
|
16504
|
+
anchor -= 1;
|
|
16505
|
+
}
|
|
16506
|
+
break;
|
|
16507
|
+
}
|
|
16508
|
+
if (message?.role === "user") {
|
|
16509
|
+
anchor = i;
|
|
16510
|
+
break;
|
|
16511
|
+
}
|
|
16512
|
+
}
|
|
16513
|
+
if (anchor < 0) break;
|
|
16514
|
+
prefix.splice(anchor);
|
|
16515
|
+
remaining -= 1;
|
|
16516
|
+
}
|
|
16517
|
+
messages.splice(0, messages.length, ...prefix, ...tail);
|
|
16518
|
+
}
|
|
16519
|
+
function classifyUndoAnchor(origin) {
|
|
16520
|
+
if (origin === void 0 || origin === null) return "user";
|
|
16521
|
+
if (typeof origin !== "object") return "other";
|
|
16522
|
+
const kind = origin.kind;
|
|
16523
|
+
if (kind === "user") return "user";
|
|
16524
|
+
if (kind === "skill_activation" && origin.trigger === "user-slash") return "skill";
|
|
16525
|
+
return "other";
|
|
16526
|
+
}
|
|
16527
|
+
/** Match the core `/undo` anchor rule without importing agent-core internals. */
|
|
16528
|
+
function countUndoAnchors(history) {
|
|
16529
|
+
let count = 0;
|
|
16530
|
+
for (const message of history) {
|
|
16531
|
+
if (message.role !== "user") continue;
|
|
16532
|
+
const origin = message.origin;
|
|
16533
|
+
if (origin === void 0 || origin.kind === "user" || origin.kind === "skill_activation" && origin.trigger === "user-slash") count += 1;
|
|
16534
|
+
}
|
|
16535
|
+
return count;
|
|
16536
|
+
}
|
|
16472
16537
|
const execFileAsync = promisify(execFile);
|
|
16473
16538
|
/** Raw porcelain rows: repo-root-relative path + XY status. `getGitStatus`
|
|
16474
16539
|
* enriches these into full `GitFileChange` records. */
|
|
@@ -16731,6 +16796,8 @@ var WebSession = class {
|
|
|
16731
16796
|
heartbeatTimer = null;
|
|
16732
16797
|
unsubscribe = null;
|
|
16733
16798
|
busy = false;
|
|
16799
|
+
/** Prevent two concurrent REST `/undo` calls from mutating core history together. */
|
|
16800
|
+
undoInFlight = false;
|
|
16734
16801
|
/**
|
|
16735
16802
|
* In-flight turn count across all agents (main + subagents). Used by the
|
|
16736
16803
|
* idle-exit watchdog: a single boolean is not enough because a subagent's
|
|
@@ -16819,13 +16886,13 @@ var WebSession = class {
|
|
|
16819
16886
|
});
|
|
16820
16887
|
return entries;
|
|
16821
16888
|
}
|
|
16822
|
-
async persistEntry(entry) {
|
|
16889
|
+
async persistEntry(entry, throwOnError = false) {
|
|
16823
16890
|
if (!this.homeDir) return;
|
|
16824
16891
|
const line = JSON.stringify({
|
|
16825
16892
|
type: "journal",
|
|
16826
16893
|
...entry
|
|
16827
16894
|
});
|
|
16828
|
-
await appendJournalLine(this.homeDir, this.sessionId, line);
|
|
16895
|
+
await appendJournalLine(this.homeDir, this.sessionId, line, throwOnError);
|
|
16829
16896
|
}
|
|
16830
16897
|
async persistUserMessage(text, beforeSeq, clientMessageId) {
|
|
16831
16898
|
this.userMessages.push({
|
|
@@ -16882,6 +16949,9 @@ var WebSession = class {
|
|
|
16882
16949
|
* journal holds no message records.
|
|
16883
16950
|
*/
|
|
16884
16951
|
seededMessages = [];
|
|
16952
|
+
/** Undo markers already reflected by a core-history seed must not be applied twice. */
|
|
16953
|
+
seedCoveredUndoMarkers = /* @__PURE__ */ new Set();
|
|
16954
|
+
historySeeded = false;
|
|
16885
16955
|
/**
|
|
16886
16956
|
* Mark the journal untrustworthy (byte-interleaving damage, see
|
|
16887
16957
|
* journal-writer.ts).
|
|
@@ -16923,7 +16993,10 @@ var WebSession = class {
|
|
|
16923
16993
|
* monotonic with live events and consistent with the getMessagesOlder cursor.
|
|
16924
16994
|
*/
|
|
16925
16995
|
seedHistory(messages) {
|
|
16926
|
-
if (
|
|
16996
|
+
if (this.historySeeded) return;
|
|
16997
|
+
this.historySeeded = true;
|
|
16998
|
+
for (const entry of this.journal) if (entry.payload.type === "web.history.undone") this.seedCoveredUndoMarkers.add(entry.seq);
|
|
16999
|
+
if (messages.length === 0) return;
|
|
16927
17000
|
let seq = 1;
|
|
16928
17001
|
for (const message of messages) {
|
|
16929
17002
|
this.seededMessages.push({
|
|
@@ -16970,7 +17043,7 @@ var WebSession = class {
|
|
|
16970
17043
|
* clients. Used for server-owned reconstruction material (finalized
|
|
16971
17044
|
* assistant snapshots) that clients never consume as live events.
|
|
16972
17045
|
*/
|
|
16973
|
-
appendDurableSilent(payload) {
|
|
17046
|
+
appendDurableSilent(payload, throwOnPersistenceError = false) {
|
|
16974
17047
|
const entry = {
|
|
16975
17048
|
seq: this.nextSeq++,
|
|
16976
17049
|
epoch: this.epoch,
|
|
@@ -16978,8 +17051,12 @@ var WebSession = class {
|
|
|
16978
17051
|
payload
|
|
16979
17052
|
};
|
|
16980
17053
|
this.journal.push(entry);
|
|
16981
|
-
this.
|
|
16982
|
-
|
|
17054
|
+
const persisted = this.persistEntry(entry, throwOnPersistenceError);
|
|
17055
|
+
this.trackPending(persisted);
|
|
17056
|
+
return {
|
|
17057
|
+
entry,
|
|
17058
|
+
persisted
|
|
17059
|
+
};
|
|
16983
17060
|
}
|
|
16984
17061
|
subscribeEvents() {
|
|
16985
17062
|
if (!this.session) return;
|
|
@@ -17308,13 +17385,14 @@ var WebSession = class {
|
|
|
17308
17385
|
});
|
|
17309
17386
|
}
|
|
17310
17387
|
async handleCommand(ws, command, args, pendingMsgId) {
|
|
17311
|
-
const ok = (message) => {
|
|
17388
|
+
const ok = (message, extra) => {
|
|
17312
17389
|
this.broadcast({
|
|
17313
17390
|
type: "command_result",
|
|
17314
17391
|
command,
|
|
17315
17392
|
ok: true,
|
|
17316
17393
|
message,
|
|
17317
|
-
pendingMsgId
|
|
17394
|
+
pendingMsgId,
|
|
17395
|
+
...extra
|
|
17318
17396
|
}, false);
|
|
17319
17397
|
};
|
|
17320
17398
|
const fail = (message) => {
|
|
@@ -17350,9 +17428,10 @@ var WebSession = class {
|
|
|
17350
17428
|
}
|
|
17351
17429
|
return;
|
|
17352
17430
|
case "auto":
|
|
17431
|
+
case "ask":
|
|
17353
17432
|
case "yes":
|
|
17354
17433
|
case "bot": {
|
|
17355
|
-
const mode = command === "auto" ? "auto" : command === "bot" ? "bot" : "yolo";
|
|
17434
|
+
const mode = command === "auto" ? "auto" : command === "ask" ? "ask" : command === "bot" ? "bot" : "yolo";
|
|
17356
17435
|
try {
|
|
17357
17436
|
await this.session.setPermission(mode);
|
|
17358
17437
|
this.permission = mode;
|
|
@@ -17395,7 +17474,7 @@ var WebSession = class {
|
|
|
17395
17474
|
fail("Fork 失败:无法复制当前会话。");
|
|
17396
17475
|
return;
|
|
17397
17476
|
}
|
|
17398
|
-
ok(`会话已 fork,新会话 ID:${result.sessionId}
|
|
17477
|
+
ok(`会话已 fork,新会话 ID:${result.sessionId}`, { sessionId: result.sessionId });
|
|
17399
17478
|
} catch (error) {
|
|
17400
17479
|
fail(`Fork 失败:${errMsg(error)}`);
|
|
17401
17480
|
}
|
|
@@ -17506,6 +17585,7 @@ var WebSession = class {
|
|
|
17506
17585
|
planMode: false,
|
|
17507
17586
|
wolfpackMode: false,
|
|
17508
17587
|
rlmEnabled: false,
|
|
17588
|
+
rlmMaxDepth: null,
|
|
17509
17589
|
contextTokens: 0,
|
|
17510
17590
|
maxContextTokens: 0,
|
|
17511
17591
|
contextUsage: 0
|
|
@@ -17682,6 +17762,64 @@ var WebSession = class {
|
|
|
17682
17762
|
if (!this.session) throw new HttpError(409, "Session is archived (read-only)", ErrorCodes.SESSION_CLOSED);
|
|
17683
17763
|
return this.session;
|
|
17684
17764
|
}
|
|
17765
|
+
/**
|
|
17766
|
+
* Undo user turns in the core session and append a durable web projection
|
|
17767
|
+
* marker. The core RPC deliberately has no UI knowledge, so without the
|
|
17768
|
+
* marker the old append-only web journal would resurrect the removed turns
|
|
17769
|
+
* on the next snapshot or after a restart.
|
|
17770
|
+
*/
|
|
17771
|
+
async undoHistory(count) {
|
|
17772
|
+
if (!Number.isSafeInteger(count) || count < 1) throw new HttpError(400, "Undo count must be a positive integer.");
|
|
17773
|
+
if (this.busy) throw new HttpError(409, "Session is busy", ErrorCodes.TURN_AGENT_BUSY);
|
|
17774
|
+
const session = this.requireLiveSession();
|
|
17775
|
+
if (this.undoInFlight) throw new HttpError(409, "Undo already in progress");
|
|
17776
|
+
this.undoInFlight = true;
|
|
17777
|
+
try {
|
|
17778
|
+
const beforeSeq = this.nextSeq;
|
|
17779
|
+
let beforeAnchors = null;
|
|
17780
|
+
try {
|
|
17781
|
+
beforeAnchors = countUndoAnchors((await session.getContext()).history);
|
|
17782
|
+
} catch (error) {
|
|
17783
|
+
log.warn("web: failed to read context before undo", {
|
|
17784
|
+
sessionId: this.sessionId,
|
|
17785
|
+
error: String(error)
|
|
17786
|
+
});
|
|
17787
|
+
}
|
|
17788
|
+
await session.undoHistory(count);
|
|
17789
|
+
let removed = count;
|
|
17790
|
+
if (beforeAnchors !== null) try {
|
|
17791
|
+
const afterAnchors = countUndoAnchors((await session.getContext()).history);
|
|
17792
|
+
removed = Math.max(0, beforeAnchors - afterAnchors);
|
|
17793
|
+
} catch (error) {
|
|
17794
|
+
log.warn("web: failed to read context after undo", {
|
|
17795
|
+
sessionId: this.sessionId,
|
|
17796
|
+
error: String(error)
|
|
17797
|
+
});
|
|
17798
|
+
}
|
|
17799
|
+
if (removed <= 0) {
|
|
17800
|
+
await this.refreshStatus();
|
|
17801
|
+
this.broadcastStatus();
|
|
17802
|
+
return;
|
|
17803
|
+
}
|
|
17804
|
+
const { entry, persisted } = this.appendDurableSilent({
|
|
17805
|
+
type: "web.history.undone",
|
|
17806
|
+
count: removed,
|
|
17807
|
+
beforeSeq
|
|
17808
|
+
}, true);
|
|
17809
|
+
await persisted;
|
|
17810
|
+
this.broadcast({
|
|
17811
|
+
type: "event",
|
|
17812
|
+
seq: entry.seq,
|
|
17813
|
+
epoch: entry.epoch,
|
|
17814
|
+
payload: entry.payload
|
|
17815
|
+
}, false);
|
|
17816
|
+
this.trackPending(this.updateTitle());
|
|
17817
|
+
await this.refreshStatus();
|
|
17818
|
+
this.broadcastStatus();
|
|
17819
|
+
} finally {
|
|
17820
|
+
this.undoInFlight = false;
|
|
17821
|
+
}
|
|
17822
|
+
}
|
|
17685
17823
|
requireGoalSession(allowBusy = false) {
|
|
17686
17824
|
if (!this.session) throw new HttpError(409, "Session is archived (read-only)", ErrorCodes.SESSION_CLOSED);
|
|
17687
17825
|
if (!allowBusy && this.busy) throw new HttpError(409, "Session is busy", ErrorCodes.TURN_AGENT_BUSY);
|
|
@@ -17759,6 +17897,7 @@ var WebSession = class {
|
|
|
17759
17897
|
role: "assistant",
|
|
17760
17898
|
content: "",
|
|
17761
17899
|
tools: [],
|
|
17900
|
+
undoAnchor: classifyUndoAnchor(event.origin),
|
|
17762
17901
|
seq: entry.seq,
|
|
17763
17902
|
model: this.cachedStatus?.model,
|
|
17764
17903
|
turnStats: {
|
|
@@ -17812,13 +17951,20 @@ var WebSession = class {
|
|
|
17812
17951
|
break;
|
|
17813
17952
|
case "web.message.finalized": {
|
|
17814
17953
|
const finalized = event.message;
|
|
17815
|
-
|
|
17954
|
+
const lastSkeletonSeq = messages.length > 0 && messages.at(-1)?.role === "assistant" ? messages.at(-1).seq : entry.seq;
|
|
17955
|
+
if (finalized.undoAnchor === void 0 && messages.at(-1)?.role === "assistant") finalized.undoAnchor = messages.at(-1).undoAnchor;
|
|
17956
|
+
finalized.seq = lastSkeletonSeq;
|
|
17816
17957
|
if (messages.length > 0 && messages.at(-1)?.role === "assistant") messages[messages.length - 1] = finalized;
|
|
17817
17958
|
else messages.push(finalized);
|
|
17818
17959
|
currentAssistant = null;
|
|
17819
17960
|
turnFinalized = true;
|
|
17820
17961
|
break;
|
|
17821
17962
|
}
|
|
17963
|
+
case "web.history.undone":
|
|
17964
|
+
if (!this.seedCoveredUndoMarkers.has(entry.seq)) removeLastTurnsBefore(messages, event.count, event.beforeSeq);
|
|
17965
|
+
currentAssistant = null;
|
|
17966
|
+
turnFinalized = false;
|
|
17967
|
+
break;
|
|
17822
17968
|
case "turn.ended":
|
|
17823
17969
|
if (event.reason === "failed" && currentAssistant) currentAssistant.isError = true;
|
|
17824
17970
|
if (currentAssistant && !turnFinalized && currentAssistant.content === "") currentAssistant.degraded = true;
|
|
@@ -17830,7 +17976,10 @@ var WebSession = class {
|
|
|
17830
17976
|
}
|
|
17831
17977
|
for (let i = userIndex; i < this.userMessages.length; i++) {
|
|
17832
17978
|
const item = this.userMessages[i];
|
|
17833
|
-
if (item !== void 0) messages.push(
|
|
17979
|
+
if (item !== void 0) messages.push({
|
|
17980
|
+
...item.msg,
|
|
17981
|
+
seq: item.beforeSeq
|
|
17982
|
+
});
|
|
17834
17983
|
}
|
|
17835
17984
|
return messages;
|
|
17836
17985
|
}
|
|
@@ -18427,7 +18576,9 @@ var SessionManager = class {
|
|
|
18427
18576
|
await ws.switchRlm(enabled, maxDepth);
|
|
18428
18577
|
}
|
|
18429
18578
|
async undoHistory(sessionId, count) {
|
|
18430
|
-
|
|
18579
|
+
const ws = this.sessions.get(sessionId);
|
|
18580
|
+
if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
|
|
18581
|
+
await ws.undoHistory(count);
|
|
18431
18582
|
}
|
|
18432
18583
|
async compact(sessionId, instruction) {
|
|
18433
18584
|
await this.getLiveSession(sessionId).compact(instruction ? { instruction } : {});
|
|
@@ -18597,16 +18748,32 @@ async function handleSessionDataRoutes(req, res, url, method, resolveSession, wo
|
|
|
18597
18748
|
}
|
|
18598
18749
|
return true;
|
|
18599
18750
|
}
|
|
18600
|
-
|
|
18601
|
-
|
|
18602
|
-
|
|
18603
|
-
|
|
18604
|
-
|
|
18605
|
-
|
|
18606
|
-
|
|
18607
|
-
|
|
18751
|
+
const gitPath = url.split("?")[0];
|
|
18752
|
+
const isGitStatusRoute = gitPath === `${API_PREFIX}/git/status` && method === "GET";
|
|
18753
|
+
if (isGitStatusRoute || gitPath === `${API_PREFIX}/git/diff` && method === "GET") {
|
|
18754
|
+
const gitQuery = new URLSearchParams(url.split("?")[1] ?? "");
|
|
18755
|
+
const requestedGitSessionId = gitQuery.get("sessionId");
|
|
18756
|
+
let gitWorkDir = workDir;
|
|
18757
|
+
if (requestedGitSessionId !== null) {
|
|
18758
|
+
const gitSession = resolveSession(requestedGitSessionId);
|
|
18759
|
+
if (!gitSession) {
|
|
18760
|
+
sendJson(res, 404, {
|
|
18761
|
+
code: ErrorCodes.SESSION_NOT_FOUND,
|
|
18762
|
+
message: "Session not found"
|
|
18763
|
+
});
|
|
18764
|
+
return true;
|
|
18765
|
+
}
|
|
18766
|
+
gitWorkDir = gitSession.workDir;
|
|
18767
|
+
}
|
|
18768
|
+
if (isGitStatusRoute) {
|
|
18769
|
+
const gs = await getGitStatus(gitWorkDir);
|
|
18770
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
18771
|
+
res.end(JSON.stringify(gs));
|
|
18772
|
+
return true;
|
|
18773
|
+
}
|
|
18774
|
+
const relPath = gitQuery.get("path") ?? "";
|
|
18608
18775
|
try {
|
|
18609
|
-
const result = await getGitFileDiff(
|
|
18776
|
+
const result = await getGitFileDiff(gitWorkDir, relPath);
|
|
18610
18777
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
18611
18778
|
res.end(JSON.stringify({
|
|
18612
18779
|
path: relPath,
|
|
@@ -18817,6 +18984,7 @@ async function handleSessionControlRoutes(req, res, url, method, manager) {
|
|
|
18817
18984
|
return false;
|
|
18818
18985
|
}
|
|
18819
18986
|
async function handleResourceRoutes(req, res, url, method, manager) {
|
|
18987
|
+
const routePath = url.split("?")[0] ?? url;
|
|
18820
18988
|
const skillsMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/skills$`).exec(url);
|
|
18821
18989
|
if (skillsMatch && method === "GET") {
|
|
18822
18990
|
try {
|
|
@@ -19004,7 +19172,7 @@ async function handleResourceRoutes(req, res, url, method, manager) {
|
|
|
19004
19172
|
}
|
|
19005
19173
|
return true;
|
|
19006
19174
|
}
|
|
19007
|
-
const tasksMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks$`).exec(
|
|
19175
|
+
const tasksMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks$`).exec(routePath);
|
|
19008
19176
|
if (tasksMatch && method === "GET") {
|
|
19009
19177
|
try {
|
|
19010
19178
|
const query = new URLSearchParams(url.split("?")[1] ?? "");
|
|
@@ -19016,7 +19184,7 @@ async function handleResourceRoutes(req, res, url, method, manager) {
|
|
|
19016
19184
|
}
|
|
19017
19185
|
return true;
|
|
19018
19186
|
}
|
|
19019
|
-
const taskOutputMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks/([^/]+)/output$`).exec(
|
|
19187
|
+
const taskOutputMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks/([^/]+)/output$`).exec(routePath);
|
|
19020
19188
|
if (taskOutputMatch && method === "GET") {
|
|
19021
19189
|
try {
|
|
19022
19190
|
const query = new URLSearchParams(url.split("?")[1] ?? "");
|
|
@@ -19027,7 +19195,7 @@ async function handleResourceRoutes(req, res, url, method, manager) {
|
|
|
19027
19195
|
}
|
|
19028
19196
|
return true;
|
|
19029
19197
|
}
|
|
19030
|
-
const taskStopMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks/([^/]+)/stop$`).exec(
|
|
19198
|
+
const taskStopMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks/([^/]+)/stop$`).exec(routePath);
|
|
19031
19199
|
if (taskStopMatch && method === "POST") {
|
|
19032
19200
|
try {
|
|
19033
19201
|
const body = await readJsonBody(req);
|