dsh-code 1.2.0 → 1.3.0
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.en.md +5 -5
- package/README.md +5 -5
- package/lib/index.mjs +955 -233
- package/lib/startup.mjs +1 -1
- package/lib/{theme-7u5Qo3dF.mjs → theme-B3orFUYz.mjs} +8 -0
- package/lib/types/app.d.ts +20 -0
- package/lib/types/attachments.d.ts +16 -7
- package/lib/types/history.d.ts +10 -0
- package/lib/types/index.d.ts +1 -1
- package/lib/types/kernel-panels.d.ts +3 -1
- package/lib/types/locales/en.d.ts +49 -1
- package/lib/types/render/status.d.ts +4 -1
- package/lib/types/session-directory.d.ts +25 -1
- package/lib/types/session-switch.d.ts +8 -0
- package/lib/types/update-panel.d.ts +22 -1
- package/lib/types/version.d.ts +2 -0
- package/package.json +7 -5
- package/src/app.ts +288 -166
- package/src/attachments.ts +65 -19
- package/src/authorization-panel.ts +5 -2
- package/src/fork.ts +11 -7
- package/src/history.ts +14 -0
- package/src/index.ts +92 -53
- package/src/input-split.ts +24 -4
- package/src/kernel-panels.ts +60 -27
- package/src/locales/en.ts +50 -1
- package/src/locales/zh.ts +50 -1
- package/src/rainbow.ts +13 -3
- package/src/render/status.ts +80 -29
- package/src/render/text.ts +2 -1
- package/src/session-directory.ts +82 -3
- package/src/session-switch.ts +14 -0
- package/src/store.ts +19 -1
- package/src/update-panel.ts +112 -5
- package/src/version.ts +5 -0
package/lib/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as __require, r as __toESM, t as __commonJSMin } from "./rolldown-runtime-CMFfr-1z.mjs";
|
|
2
|
-
import { _ as parseRainbowArgument, a as dim, b as rerollRainbow, c as inkColor, d as parseThemeName, f as promptRowTokens, g as themeFlow, h as surfaceAccent, i as diffBackground, l as isPrismatic, m as setTheme, n as THEMES, o as getPalette, p as rowBackground, s as getTheme, t as FLOW_ANCHORS, u as isRainbow, v as rainbowRoll, x as chalk, y as rainbowSeedLabel } from "./theme-
|
|
2
|
+
import { _ as parseRainbowArgument, a as dim, b as rerollRainbow, c as inkColor, d as parseThemeName, f as promptRowTokens, g as themeFlow, h as surfaceAccent, i as diffBackground, l as isPrismatic, m as setTheme, n as THEMES, o as getPalette, p as rowBackground, s as getTheme, t as FLOW_ANCHORS, u as isRainbow, v as rainbowRoll, x as chalk, y as rainbowSeedLabel } from "./theme-B3orFUYz.mjs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import * as fs from "node:fs";
|
|
@@ -12,6 +12,7 @@ import { installModelSelection } from "@deepseek-ai/dsh-agent";
|
|
|
12
12
|
import { MessageId, ReasoningEffortId, assistantStreamFirstTokenTime, boundContextSummary, createUserMessage, isTokenDelta, normalizeApiKey } from "@deepseek-ai/dsh-llm";
|
|
13
13
|
import { SESSION_FORMAT_VERSION, SessionId, SessionLogOffset } from "@deepseek-ai/dsh-session";
|
|
14
14
|
import { deriveTurnTokenUsage } from "@deepseek-ai/dsh-token-meter/client";
|
|
15
|
+
import { SessionAlreadyOwnedError } from "@deepseek-ai/dsh-session-persistence";
|
|
15
16
|
import { PassThrough, Stream } from "node:stream";
|
|
16
17
|
import process$1, { cwd, env } from "node:process";
|
|
17
18
|
import { EventEmitter } from "node:events";
|
|
@@ -24607,7 +24608,7 @@ function stringWidth(text) {
|
|
|
24607
24608
|
function formatTokens(n) {
|
|
24608
24609
|
const scaled = (v) => v >= 100 ? String(Math.round(v)) : String(Math.round(v * 10) / 10);
|
|
24609
24610
|
if (n < 1e3) return String(n);
|
|
24610
|
-
if (n <
|
|
24611
|
+
if (n < 999500) return scaled(n / 1e3) + "K";
|
|
24611
24612
|
return scaled(n / 1e6) + "M";
|
|
24612
24613
|
}
|
|
24613
24614
|
/** C0 controls except tab (0x09) and newline (0x0a), plus DEL and C1. */
|
|
@@ -24779,7 +24780,37 @@ const en = {
|
|
|
24779
24780
|
"search.compact.searching": "searching…",
|
|
24780
24781
|
"search.compact.error": "error: {message}",
|
|
24781
24782
|
"search.footer": "↑↓ move · enter search/resume · esc close",
|
|
24783
|
+
"header.hint": "/help · Esc interrupt · Ctrl+C quit",
|
|
24784
|
+
"header.hintResumed": "resumed · /help · Esc interrupt",
|
|
24782
24785
|
"statusline.title": "/statusline · items apply to the live status line below",
|
|
24786
|
+
"statusline.item.model": "model",
|
|
24787
|
+
"statusline.item.model.desc": "provider/model serving this session",
|
|
24788
|
+
"statusline.item.cwd": "cwd",
|
|
24789
|
+
"statusline.item.cwd.desc": "working-directory basename",
|
|
24790
|
+
"statusline.item.mode": "mode",
|
|
24791
|
+
"statusline.item.mode.desc": "agent preset composing the session",
|
|
24792
|
+
"statusline.item.branch": "branch",
|
|
24793
|
+
"statusline.item.branch.desc": "git branch inside a repository",
|
|
24794
|
+
"statusline.item.context": "context",
|
|
24795
|
+
"statusline.item.context.desc": "context-window occupancy meter",
|
|
24796
|
+
"statusline.item.permission": "permission",
|
|
24797
|
+
"statusline.item.permission.desc": "permission preset badge with cycle hint",
|
|
24798
|
+
"statusline.item.plan": "plan",
|
|
24799
|
+
"statusline.item.plan.desc": "plan-mode state mark",
|
|
24800
|
+
"statusline.item.turns": "turns",
|
|
24801
|
+
"statusline.item.turns.desc": "turn and step counters",
|
|
24802
|
+
"statusline.item.durations": "durations",
|
|
24803
|
+
"statusline.item.durations.desc": "llm/ttft/decode/tool wall time",
|
|
24804
|
+
"statusline.item.cache": "cache",
|
|
24805
|
+
"statusline.item.cache.desc": "cache-hit share of billed input",
|
|
24806
|
+
"statusline.item.tokens": "tokens",
|
|
24807
|
+
"statusline.item.tokens.desc": "cumulative input/output tokens",
|
|
24808
|
+
"statusline.item.title": "title",
|
|
24809
|
+
"statusline.item.title.desc": "session title or short id",
|
|
24810
|
+
"statusline.item.goal": "goal",
|
|
24811
|
+
"statusline.item.goal.desc": "live goal phase and round progress",
|
|
24812
|
+
"statusline.item.sandbox": "sandbox",
|
|
24813
|
+
"statusline.item.sandbox.desc": "divergent sandbox-mode override",
|
|
24783
24814
|
"schedule.title": "/schedule · {count} active reminder",
|
|
24784
24815
|
"schedule.titlePlural": "/schedule · {count} active reminders",
|
|
24785
24816
|
"schedule.empty": "no active reminders — the model creates them with schedule_create",
|
|
@@ -24925,10 +24956,16 @@ const en = {
|
|
|
24925
24956
|
"status.label.out": "out",
|
|
24926
24957
|
"status.label.mode": "/mode",
|
|
24927
24958
|
"status.label.context": "context",
|
|
24959
|
+
"status.label.sandbox": "sandbox",
|
|
24960
|
+
"status.plan.on": "plan on",
|
|
24961
|
+
"status.plan.mark": "⧉ plan",
|
|
24962
|
+
"status.goal.round": "◎ round {current}/{max}",
|
|
24963
|
+
"status.goal.phase": "◎ {phase}",
|
|
24928
24964
|
"frozen.keysGoTo": "keys go to {owner} · esc {action}",
|
|
24929
24965
|
"frozen.action.rejects": "rejects",
|
|
24930
24966
|
"frozen.action.cancels": "cancels",
|
|
24931
24967
|
"frozen.action.closes": "closes",
|
|
24968
|
+
"frozen.action.waits": "waits",
|
|
24932
24969
|
"time.justNow": "now",
|
|
24933
24970
|
"time.minutesAgo": "{n}m ago",
|
|
24934
24971
|
"time.hoursAgo": "{n}h ago",
|
|
@@ -24940,6 +24977,7 @@ const en = {
|
|
|
24940
24977
|
"notice.themeSaveFailed": "theme save failed: {message}",
|
|
24941
24978
|
"notice.languageSaveFailed": "language save failed: {message}",
|
|
24942
24979
|
"notice.alreadyActive": "that session is already active",
|
|
24980
|
+
"notice.permissionPresetsUnmounted": "permission presets are not mounted in this composition",
|
|
24943
24981
|
"notice.queueCancelled": "queued message cancelled",
|
|
24944
24982
|
"notice.queueActionFailed": "queue action failed: {message}",
|
|
24945
24983
|
"notice.queueUnavailable": "queued message is no longer pending",
|
|
@@ -24954,6 +24992,8 @@ const en = {
|
|
|
24954
24992
|
"notice.usage.animation": "usage: /animation [on|off]",
|
|
24955
24993
|
"notice.usage.language": "usage: /language [en|zh]",
|
|
24956
24994
|
"notice.usage.rainbow": "usage: /rainbow [seed]",
|
|
24995
|
+
"notice.usage.bareCommand": "usage: /{name}",
|
|
24996
|
+
"notice.switchInProgress": "switching sessions — send again once the new session is up, or /resume cancel",
|
|
24957
24997
|
"notice.imageCancelled": "image submission cancelled",
|
|
24958
24998
|
"notice.themeConfigUnreadable": "theme config unreadable, using dark: {message}",
|
|
24959
24999
|
"notice.languageConfigUnreadable": "language config unreadable, using english: {message}",
|
|
@@ -24984,6 +25024,13 @@ const en = {
|
|
|
24984
25024
|
"notice.resumeFailed": "resume failed: {message}",
|
|
24985
25025
|
"notice.forkFailed": "fork failed: {message}",
|
|
24986
25026
|
"notice.sessionSwitchFailed": "session switch failed: {message}",
|
|
25027
|
+
"notice.sessionCreated": "created {id} · mode {mode}",
|
|
25028
|
+
"notice.sessionResumed": "resumed {id} · mode {mode}",
|
|
25029
|
+
"notice.sessionSwitchedDirty": "switched to {id}, but {detail}",
|
|
25030
|
+
"notice.flushFailed": "previous session flush failed: {message}",
|
|
25031
|
+
"notice.agentReleaseFailed": "previous agent release failed: {message}",
|
|
25032
|
+
"notice.copyEmpty": "nothing to copy yet",
|
|
25033
|
+
"notice.copied": "copied latest response",
|
|
24987
25034
|
"notice.switchQueued": "will switch to {label} when the current turn finishes · /resume cancel to abort",
|
|
24988
25035
|
"notice.reviewStarted": "review started under read-only permissions",
|
|
24989
25036
|
"notice.reviewFailed": "review failed: {message}",
|
|
@@ -25035,7 +25082,8 @@ const en = {
|
|
|
25035
25082
|
"panel.footer.chooseSelect": "↑↓ choose · enter select · r refresh · esc close",
|
|
25036
25083
|
"panel.footer.inspectDetails": "↑↓ inspect · enter details · r refresh · esc close",
|
|
25037
25084
|
"panel.footer.inspectRefresh": "↑↓ inspect · r refresh · esc close",
|
|
25038
|
-
"panel.footer.resume": "tab/←→ filters · ↑↓/pg navigate · ctrl+f search · e details · t transcript ·
|
|
25085
|
+
"panel.footer.resume": "tab/←→ filters · ↑↓/pg navigate · ctrl+f search · e details · t transcript · enter resume",
|
|
25086
|
+
"panel.footer.delete": "tab/←→ filters · ↑↓/pg navigate · ctrl+f search · e details · t transcript · enter delete · esc close",
|
|
25039
25087
|
"panel.footer.transcript": "lines {from}-{to}/{total} · ↑↓/pg/g/G · t/esc close",
|
|
25040
25088
|
"panel.footer.history": "↑↓ move · g/G ends · enter fill · esc close",
|
|
25041
25089
|
"panel.footer.review": "↑↓ move · enter select · esc back · q close",
|
|
@@ -25047,6 +25095,7 @@ const en = {
|
|
|
25047
25095
|
"panel.jobs.title": "/jobs · background tasks · {count}",
|
|
25048
25096
|
"panel.resume.deleteTitle": "permanently delete {target}? this cannot be undone · subagent threads go too",
|
|
25049
25097
|
"panel.resume.title": "/resume{mode}{search} · {toolbar}",
|
|
25098
|
+
"panel.delete.title": "/delete{search} · {toolbar}",
|
|
25050
25099
|
"panel.noActiveReminders": "no active reminders — the model schedules via schedule_create",
|
|
25051
25100
|
"panel.notMounted": "not mounted",
|
|
25052
25101
|
"panel.broken": "broken: {message}",
|
|
@@ -25222,7 +25271,37 @@ const zh = {
|
|
|
25222
25271
|
"search.compact.searching": "搜索中…",
|
|
25223
25272
|
"search.compact.error": "错误:{message}",
|
|
25224
25273
|
"search.footer": "↑↓ 移动 · 回车 搜索/恢复 · esc 关闭",
|
|
25274
|
+
"header.hint": "/help · Esc 中断 · Ctrl+C 退出",
|
|
25275
|
+
"header.hintResumed": "已恢复 · /help · Esc 中断",
|
|
25225
25276
|
"statusline.title": "/statusline · 选项实时应用到下方状态栏",
|
|
25277
|
+
"statusline.item.model": "模型",
|
|
25278
|
+
"statusline.item.model.desc": "本会话使用的 provider/model",
|
|
25279
|
+
"statusline.item.cwd": "目录",
|
|
25280
|
+
"statusline.item.cwd.desc": "工作目录基名",
|
|
25281
|
+
"statusline.item.mode": "模式",
|
|
25282
|
+
"statusline.item.mode.desc": "当前 agent 预设",
|
|
25283
|
+
"statusline.item.branch": "分支",
|
|
25284
|
+
"statusline.item.branch.desc": "仓库内 git 分支",
|
|
25285
|
+
"statusline.item.context": "上下文",
|
|
25286
|
+
"statusline.item.context.desc": "上下文窗口占用",
|
|
25287
|
+
"statusline.item.permission": "权限",
|
|
25288
|
+
"statusline.item.permission.desc": "权限预设徽章与循环提示",
|
|
25289
|
+
"statusline.item.plan": "计划",
|
|
25290
|
+
"statusline.item.plan.desc": "计划模式标记",
|
|
25291
|
+
"statusline.item.turns": "回合",
|
|
25292
|
+
"statusline.item.turns.desc": "回合与步骤计数",
|
|
25293
|
+
"statusline.item.durations": "耗时",
|
|
25294
|
+
"statusline.item.durations.desc": "模型/首字/解码/工具墙钟",
|
|
25295
|
+
"statusline.item.cache": "缓存",
|
|
25296
|
+
"statusline.item.cache.desc": "计费输入的缓存命中占比",
|
|
25297
|
+
"statusline.item.tokens": "用量",
|
|
25298
|
+
"statusline.item.tokens.desc": "累计输入/输出 token",
|
|
25299
|
+
"statusline.item.title": "标题",
|
|
25300
|
+
"statusline.item.title.desc": "会话标题或短 id",
|
|
25301
|
+
"statusline.item.goal": "目标",
|
|
25302
|
+
"statusline.item.goal.desc": "进行中的目标阶段与轮次",
|
|
25303
|
+
"statusline.item.sandbox": "沙箱",
|
|
25304
|
+
"statusline.item.sandbox.desc": "与预设不同的沙箱覆盖",
|
|
25226
25305
|
"schedule.title": "/schedule · {count} 条活动提醒",
|
|
25227
25306
|
"schedule.titlePlural": "/schedule · {count} 条活动提醒",
|
|
25228
25307
|
"schedule.empty": "暂无活动提醒——模型通过 schedule_create 创建",
|
|
@@ -25368,10 +25447,16 @@ const zh = {
|
|
|
25368
25447
|
"status.label.out": "出",
|
|
25369
25448
|
"status.label.mode": "/mode",
|
|
25370
25449
|
"status.label.context": "上下文",
|
|
25450
|
+
"status.label.sandbox": "沙箱",
|
|
25451
|
+
"status.plan.on": "计划开",
|
|
25452
|
+
"status.plan.mark": "⧉ 计划",
|
|
25453
|
+
"status.goal.round": "◎ 第 {current}/{max} 轮",
|
|
25454
|
+
"status.goal.phase": "◎ {phase}",
|
|
25371
25455
|
"frozen.keysGoTo": "按键交给{owner} · esc {action}",
|
|
25372
25456
|
"frozen.action.rejects": "拒绝",
|
|
25373
25457
|
"frozen.action.cancels": "取消",
|
|
25374
25458
|
"frozen.action.closes": "关闭",
|
|
25459
|
+
"frozen.action.waits": "等待",
|
|
25375
25460
|
"time.justNow": "刚刚",
|
|
25376
25461
|
"time.minutesAgo": "{n} 分钟前",
|
|
25377
25462
|
"time.hoursAgo": "{n} 小时前",
|
|
@@ -25383,6 +25468,7 @@ const zh = {
|
|
|
25383
25468
|
"notice.themeSaveFailed": "主题保存失败:{message}",
|
|
25384
25469
|
"notice.languageSaveFailed": "语言保存失败:{message}",
|
|
25385
25470
|
"notice.alreadyActive": "该会话已是当前会话",
|
|
25471
|
+
"notice.permissionPresetsUnmounted": "当前组合未挂载权限预设",
|
|
25386
25472
|
"notice.queueCancelled": "已取消排队消息",
|
|
25387
25473
|
"notice.queueActionFailed": "队列操作失败:{message}",
|
|
25388
25474
|
"notice.queueUnavailable": "该消息已不在队列中",
|
|
@@ -25397,6 +25483,8 @@ const zh = {
|
|
|
25397
25483
|
"notice.usage.animation": "用法:/animation [on|off]",
|
|
25398
25484
|
"notice.usage.language": "用法:/language [en|zh]",
|
|
25399
25485
|
"notice.usage.rainbow": "用法:/rainbow [seed]",
|
|
25486
|
+
"notice.usage.bareCommand": "用法:/{name}",
|
|
25487
|
+
"notice.switchInProgress": "正在切换会话 —— 等新会话就绪后再发送,或用 /resume cancel 取消",
|
|
25400
25488
|
"notice.imageCancelled": "图片提交已取消",
|
|
25401
25489
|
"notice.themeConfigUnreadable": "主题配置无法读取,使用暗色:{message}",
|
|
25402
25490
|
"notice.languageConfigUnreadable": "语言配置无法读取,使用英文:{message}",
|
|
@@ -25427,6 +25515,13 @@ const zh = {
|
|
|
25427
25515
|
"notice.resumeFailed": "恢复失败:{message}",
|
|
25428
25516
|
"notice.forkFailed": "分叉失败:{message}",
|
|
25429
25517
|
"notice.sessionSwitchFailed": "会话切换失败:{message}",
|
|
25518
|
+
"notice.sessionCreated": "已创建 {id} · 模式 {mode}",
|
|
25519
|
+
"notice.sessionResumed": "已恢复 {id} · 模式 {mode}",
|
|
25520
|
+
"notice.sessionSwitchedDirty": "已切换到 {id},但 {detail}",
|
|
25521
|
+
"notice.flushFailed": "上一会话刷新失败:{message}",
|
|
25522
|
+
"notice.agentReleaseFailed": "上一代理释放失败:{message}",
|
|
25523
|
+
"notice.copyEmpty": "还没有可复制的回复",
|
|
25524
|
+
"notice.copied": "已复制最近一条回复",
|
|
25430
25525
|
"notice.switchQueued": "当前回合结束后将切换到 {label} · 可用 /resume cancel 取消",
|
|
25431
25526
|
"notice.reviewStarted": "审查已在只读权限下开始",
|
|
25432
25527
|
"notice.reviewFailed": "审查失败:{message}",
|
|
@@ -25478,7 +25573,8 @@ const zh = {
|
|
|
25478
25573
|
"panel.footer.chooseSelect": "↑↓ 移动 · 回车 选择 · r 刷新 · esc 关闭",
|
|
25479
25574
|
"panel.footer.inspectDetails": "↑↓ 查看 · 回车 详情 · r 刷新 · esc 关闭",
|
|
25480
25575
|
"panel.footer.inspectRefresh": "↑↓ 查看 · r 刷新 · esc 关闭",
|
|
25481
|
-
"panel.footer.resume": "tab/←→ 筛选 · ↑↓/pg 翻页 · ctrl+f 搜索 · e 详情 · t 转录 ·
|
|
25576
|
+
"panel.footer.resume": "tab/←→ 筛选 · ↑↓/pg 翻页 · ctrl+f 搜索 · e 详情 · t 转录 · 回车恢复",
|
|
25577
|
+
"panel.footer.delete": "tab/←→ 筛选 · ↑↓/pg 翻页 · ctrl+f 搜索 · e 详情 · t 转录 · 回车删除 · esc 关闭",
|
|
25482
25578
|
"panel.footer.transcript": "行 {from}-{to}/{total} · ↑↓/pg/g/G · t/esc 关闭",
|
|
25483
25579
|
"panel.footer.history": "↑↓ 移动 · g/G 首尾 · 回车填入 · esc 关闭",
|
|
25484
25580
|
"panel.footer.review": "↑↓ 移动 · 回车选择 · esc 返回 · q 关闭",
|
|
@@ -25490,6 +25586,7 @@ const zh = {
|
|
|
25490
25586
|
"panel.jobs.title": "/jobs · 后台任务 · {count}",
|
|
25491
25587
|
"panel.resume.deleteTitle": "永久删除 {target}?此操作无法撤销,子代理线程也会删除",
|
|
25492
25588
|
"panel.resume.title": "/resume{mode}{search} · {toolbar}",
|
|
25589
|
+
"panel.delete.title": "/delete{search} · {toolbar}",
|
|
25493
25590
|
"panel.noActiveReminders": "暂无活动提醒——模型通过 schedule_create 创建",
|
|
25494
25591
|
"panel.notMounted": "未加载",
|
|
25495
25592
|
"panel.broken": "异常:{message}",
|
|
@@ -25771,6 +25868,64 @@ function LanguagePanel({ current, select, close }) {
|
|
|
25771
25868
|
function clipUpdateLines(lines) {
|
|
25772
25869
|
return lines.length <= 800 ? lines : lines.slice(lines.length - 800);
|
|
25773
25870
|
}
|
|
25871
|
+
let updateApplyJob;
|
|
25872
|
+
const updateApplyRunningListeners = /* @__PURE__ */ new Set();
|
|
25873
|
+
function emitUpdateApplyRunning(running) {
|
|
25874
|
+
for (const listener of updateApplyRunningListeners) listener(running);
|
|
25875
|
+
}
|
|
25876
|
+
/** True while an apply child is alive, even if the /update panel is closed. */
|
|
25877
|
+
function isUpdateApplyRunning() {
|
|
25878
|
+
return updateApplyJob !== void 0;
|
|
25879
|
+
}
|
|
25880
|
+
/** Subscribe to apply-running changes (frozen-hint "esc waits"). */
|
|
25881
|
+
function subscribeUpdateApplyRunning(listener) {
|
|
25882
|
+
updateApplyRunningListeners.add(listener);
|
|
25883
|
+
listener(updateApplyJob !== void 0);
|
|
25884
|
+
return () => {
|
|
25885
|
+
updateApplyRunningListeners.delete(listener);
|
|
25886
|
+
};
|
|
25887
|
+
}
|
|
25888
|
+
/** The live apply job, if any: lines already streamed plus the shared promise. */
|
|
25889
|
+
function currentUpdateApply() {
|
|
25890
|
+
return updateApplyJob;
|
|
25891
|
+
}
|
|
25892
|
+
/**
|
|
25893
|
+
* Run `apply` once. A second call while the child is alive reuses the same
|
|
25894
|
+
* promise and replays buffered lines — closing /update and opening it again
|
|
25895
|
+
* must not spawn a second `npm install`.
|
|
25896
|
+
*/
|
|
25897
|
+
function runUpdateApply(apply, plan, onLine) {
|
|
25898
|
+
if (updateApplyJob !== void 0) {
|
|
25899
|
+
if (onLine !== void 0) {
|
|
25900
|
+
for (const line of updateApplyJob.lines) onLine(line);
|
|
25901
|
+
updateApplyJob.lineListeners.add(onLine);
|
|
25902
|
+
}
|
|
25903
|
+
return updateApplyJob.promise;
|
|
25904
|
+
}
|
|
25905
|
+
const lines = [];
|
|
25906
|
+
const lineListeners = /* @__PURE__ */ new Set();
|
|
25907
|
+
if (onLine !== void 0) lineListeners.add(onLine);
|
|
25908
|
+
const promise = apply((line) => {
|
|
25909
|
+
const text = singleLineText(line);
|
|
25910
|
+
lines.push(text);
|
|
25911
|
+
if (lines.length > 800) lines.splice(0, lines.length - 800);
|
|
25912
|
+
for (const listener of lineListeners) listener(text);
|
|
25913
|
+
}, plan);
|
|
25914
|
+
updateApplyJob = {
|
|
25915
|
+
promise,
|
|
25916
|
+
lines,
|
|
25917
|
+
lineListeners
|
|
25918
|
+
};
|
|
25919
|
+
emitUpdateApplyRunning(true);
|
|
25920
|
+
const settle = () => {
|
|
25921
|
+
if (updateApplyJob?.promise === promise) {
|
|
25922
|
+
updateApplyJob = void 0;
|
|
25923
|
+
emitUpdateApplyRunning(false);
|
|
25924
|
+
}
|
|
25925
|
+
};
|
|
25926
|
+
promise.then(settle, settle);
|
|
25927
|
+
return promise;
|
|
25928
|
+
}
|
|
25774
25929
|
/** Rendered facts of one probe: current/target versions, actions, refusals. */
|
|
25775
25930
|
function updatePlanView(status) {
|
|
25776
25931
|
const rows = [];
|
|
@@ -25870,8 +26025,33 @@ function UpdatePanel({ probe, apply, close, notify }) {
|
|
|
25870
26025
|
const [applyError, setApplyError] = (0, import_react.useState)();
|
|
25871
26026
|
const [anchor, setAnchor] = (0, import_react.useState)("tail");
|
|
25872
26027
|
const [epoch, setEpoch] = (0, import_react.useState)(0);
|
|
26028
|
+
const appendLine = (line) => {
|
|
26029
|
+
setLines((previous) => clipUpdateLines([...previous, line]));
|
|
26030
|
+
};
|
|
25873
26031
|
(0, import_react.useEffect)(() => {
|
|
25874
26032
|
let disposed = false;
|
|
26033
|
+
const existing = currentUpdateApply();
|
|
26034
|
+
if (existing !== void 0) {
|
|
26035
|
+
setPhase("apply");
|
|
26036
|
+
setLines(existing.lines);
|
|
26037
|
+
setExit(void 0);
|
|
26038
|
+
setApplyError(void 0);
|
|
26039
|
+
setAnchor("tail");
|
|
26040
|
+
existing.lineListeners.add(appendLine);
|
|
26041
|
+
existing.promise.then((code) => {
|
|
26042
|
+
if (disposed) return;
|
|
26043
|
+
setExit(code);
|
|
26044
|
+
setPhase("done");
|
|
26045
|
+
}, (reason) => {
|
|
26046
|
+
if (disposed) return;
|
|
26047
|
+
setApplyError(reason instanceof Error ? reason.message : String(reason));
|
|
26048
|
+
setPhase("done");
|
|
26049
|
+
});
|
|
26050
|
+
return () => {
|
|
26051
|
+
disposed = true;
|
|
26052
|
+
existing.lineListeners.delete(appendLine);
|
|
26053
|
+
};
|
|
26054
|
+
}
|
|
25875
26055
|
setPhase("probe");
|
|
25876
26056
|
setStatus(void 0);
|
|
25877
26057
|
setProbeError(void 0);
|
|
@@ -25887,6 +26067,7 @@ function UpdatePanel({ probe, apply, close, notify }) {
|
|
|
25887
26067
|
});
|
|
25888
26068
|
return () => {
|
|
25889
26069
|
disposed = true;
|
|
26070
|
+
currentUpdateApply()?.lineListeners.delete(appendLine);
|
|
25890
26071
|
};
|
|
25891
26072
|
}, [epoch, probe]);
|
|
25892
26073
|
const stdout = useStdout().stdout;
|
|
@@ -25894,14 +26075,13 @@ function UpdatePanel({ probe, apply, close, notify }) {
|
|
|
25894
26075
|
const start = () => {
|
|
25895
26076
|
if (phase !== "plan" || status === void 0) return;
|
|
25896
26077
|
if (!updatePlanView(status).runnable) return;
|
|
26078
|
+
if (isUpdateApplyRunning()) return;
|
|
25897
26079
|
setPhase("apply");
|
|
25898
26080
|
setLines([]);
|
|
25899
26081
|
setExit(void 0);
|
|
25900
26082
|
setApplyError(void 0);
|
|
25901
26083
|
setAnchor("tail");
|
|
25902
|
-
apply((
|
|
25903
|
-
setLines((previous) => clipUpdateLines([...previous, singleLineText(line)]));
|
|
25904
|
-
}, status.plan).then((code) => {
|
|
26084
|
+
runUpdateApply(apply, status.plan, appendLine).then((code) => {
|
|
25905
26085
|
setExit(code);
|
|
25906
26086
|
setPhase("done");
|
|
25907
26087
|
notify(code === 0 ? "update installed — restart dsh to activate" : `update failed (exit ${code})`, code === 0 ? "info" : "error");
|
|
@@ -26023,6 +26203,10 @@ function readPackageVersion(manifest = new URL("../package.json", import.meta.ur
|
|
|
26023
26203
|
}
|
|
26024
26204
|
/** Version of the installed dsh-code package. */
|
|
26025
26205
|
const DSH_CODE_VERSION = readPackageVersion();
|
|
26206
|
+
/** Header brand line: hide a missing-manifest fallback so we never paint v0.0.0. */
|
|
26207
|
+
function headerBrandTitle(version = DSH_CODE_VERSION) {
|
|
26208
|
+
return version === "0.0.0" ? "DeepSeek Harness" : `DeepSeek Harness · v${version}`;
|
|
26209
|
+
}
|
|
26026
26210
|
/** The harness host package: the dsh CLI whose process runs the TUI plugin. */
|
|
26027
26211
|
const DSH_HOST_PACKAGE_NAME = "@deepseek-ai/dsh";
|
|
26028
26212
|
/** Parent levels above the host entry allowed to hold its package manifest. */
|
|
@@ -29660,6 +29844,35 @@ function createMentions(ctx, agent, cwd) {
|
|
|
29660
29844
|
//#endregion
|
|
29661
29845
|
//#region src/session-directory.ts
|
|
29662
29846
|
/** Lightweight session-directory projection for the /resume picker. */
|
|
29847
|
+
/**
|
|
29848
|
+
* Acquire every subtree member's cross-process write lease before deleting
|
|
29849
|
+
* any artifact. A partial acquisition is rolled back, so callers either hold
|
|
29850
|
+
* the whole deletion boundary or touch nothing.
|
|
29851
|
+
*/
|
|
29852
|
+
async function acquireSessionDeletionLeases(persistence, ids) {
|
|
29853
|
+
const leases = [];
|
|
29854
|
+
try {
|
|
29855
|
+
for (const id of ids) leases.push(await persistence.open(SessionId(id), "write"));
|
|
29856
|
+
return leases;
|
|
29857
|
+
} catch (error) {
|
|
29858
|
+
try {
|
|
29859
|
+
await releaseSessionDeletionLeases(leases);
|
|
29860
|
+
} catch (releaseError) {
|
|
29861
|
+
throw new AggregateError([error, releaseError], "session deletion lease acquisition and rollback failed");
|
|
29862
|
+
}
|
|
29863
|
+
throw error;
|
|
29864
|
+
}
|
|
29865
|
+
}
|
|
29866
|
+
/** Release deletion leases in reverse acquisition order. */
|
|
29867
|
+
async function releaseSessionDeletionLeases(leases) {
|
|
29868
|
+
const failures = [];
|
|
29869
|
+
for (const lease of [...leases].reverse()) try {
|
|
29870
|
+
await lease.close();
|
|
29871
|
+
} catch (error) {
|
|
29872
|
+
failures.push(error);
|
|
29873
|
+
}
|
|
29874
|
+
if (failures.length > 0) throw new AggregateError(failures, "failed to release session deletion leases");
|
|
29875
|
+
}
|
|
29663
29876
|
/** Case-insensitive filesystems (Windows, macOS) compare paths by lowercased form. */
|
|
29664
29877
|
const CASE_INSENSITIVE_FS = process.platform === "win32" || process.platform === "darwin";
|
|
29665
29878
|
/** True only for delegated subagents; ordinary forks also carry lineage. */
|
|
@@ -29695,6 +29908,23 @@ function matchSessionId(headers, wanted) {
|
|
|
29695
29908
|
if (matches.length > 1) throw new Error(`session prefix "${wanted}" is ambiguous (${matches.length} matches): use more of the id`);
|
|
29696
29909
|
return matches[0];
|
|
29697
29910
|
}
|
|
29911
|
+
/**
|
|
29912
|
+
* Unique picker-row match by exact id, unique prefix, or unique suffix.
|
|
29913
|
+
* The resume list shows `id.slice(-12)`, so `/delete` arguments are often
|
|
29914
|
+
* that tail rather than a leading prefix.
|
|
29915
|
+
*/
|
|
29916
|
+
function matchSessionRow(rows, wanted) {
|
|
29917
|
+
const needle = wanted.trim();
|
|
29918
|
+
if (needle === "") throw new Error("no persisted session matches \"\"");
|
|
29919
|
+
const exact = rows.filter((row) => row.id === needle);
|
|
29920
|
+
if (exact[0] !== void 0 && exact.length === 1) return exact[0];
|
|
29921
|
+
const prefixed = rows.filter((row) => row.id.startsWith(needle));
|
|
29922
|
+
if (prefixed[0] !== void 0 && prefixed.length === 1) return prefixed[0];
|
|
29923
|
+
const suffixed = rows.filter((row) => row.id.endsWith(needle));
|
|
29924
|
+
if (suffixed[0] !== void 0 && suffixed.length === 1) return suffixed[0];
|
|
29925
|
+
if (prefixed.length > 1 || suffixed.length > 1) throw new Error(`session prefix "${needle}" is ambiguous (${Math.max(prefixed.length, suffixed.length)} matches): use more of the id`);
|
|
29926
|
+
throw new Error(`no persisted session matches "${needle}"`);
|
|
29927
|
+
}
|
|
29698
29928
|
/** The newest persisted ROOT session pinned to this cwd, or undefined. */
|
|
29699
29929
|
function newestRootForCwd(headers, cwd) {
|
|
29700
29930
|
return headers.filter((header) => !isSubagentSession(header) && samePath(header.cwd, cwd)).sort((left, right) => right.createdAt - left.createdAt)[0];
|
|
@@ -29727,7 +29957,13 @@ function projectSessionRows(records, options, updated) {
|
|
|
29727
29957
|
persisted: record.persisted,
|
|
29728
29958
|
preset: record.header.agentPreset ?? "standard"
|
|
29729
29959
|
};
|
|
29730
|
-
}).filter((row) =>
|
|
29960
|
+
}).filter((row) => sessionRowMatchesQuery(row, needle)).sort((left, right) => options.sort === "newest" ? right.updatedAt - left.updatedAt || right.createdAt - left.createdAt : left.updatedAt - right.updatedAt || left.createdAt - right.createdAt);
|
|
29961
|
+
}
|
|
29962
|
+
/** True when the picker query hits id, path, preset, or the displayed title. */
|
|
29963
|
+
function sessionRowMatchesQuery(row, query) {
|
|
29964
|
+
const needle = query.trim().toLowerCase();
|
|
29965
|
+
if (needle === "") return true;
|
|
29966
|
+
return `${row.id} ${row.cwd} ${row.workspace} ${row.preset} ${row.title ?? ""}`.toLowerCase().includes(needle);
|
|
29731
29967
|
}
|
|
29732
29968
|
/** Merge page-local title observations without disturbing directory order. */
|
|
29733
29969
|
function mergeSessionTitles(rows, observations) {
|
|
@@ -29856,7 +30092,10 @@ function jsonlSessionRoot(persistence) {
|
|
|
29856
30092
|
*/
|
|
29857
30093
|
function collectDeletionSubtree(records, id) {
|
|
29858
30094
|
const parentOf = /* @__PURE__ */ new Map();
|
|
29859
|
-
for (const record of records)
|
|
30095
|
+
for (const record of records) {
|
|
30096
|
+
if (!isSubagentSession(record.header)) continue;
|
|
30097
|
+
parentOf.set(record.header.id, record.header.parentSession);
|
|
30098
|
+
}
|
|
29860
30099
|
const doomed = /* @__PURE__ */ new Set([id]);
|
|
29861
30100
|
for (let pass = 0; pass < 2; pass += 1) for (const candidate of parentOf.keys()) {
|
|
29862
30101
|
if (doomed.has(candidate)) continue;
|
|
@@ -30391,7 +30630,11 @@ function formatDuration(ms) {
|
|
|
30391
30630
|
const s = ms / 1e3;
|
|
30392
30631
|
if (s < 60) return String(Math.round(s * 10) / 10) + "s";
|
|
30393
30632
|
const whole = Math.round(s);
|
|
30394
|
-
|
|
30633
|
+
const hours = Math.floor(whole / 3600);
|
|
30634
|
+
const minutes = Math.floor(whole % 3600 / 60);
|
|
30635
|
+
const seconds = whole % 60;
|
|
30636
|
+
if (hours > 0) return `${hours}h${minutes}m${seconds === 0 ? "" : `${seconds}s`}`;
|
|
30637
|
+
return `${minutes}m${seconds}s`;
|
|
30395
30638
|
}
|
|
30396
30639
|
/**
|
|
30397
30640
|
* Compact decode rate: one decimal under a hundred, whole below a thousand,
|
|
@@ -30569,6 +30812,75 @@ const STATUS_ITEMS = [
|
|
|
30569
30812
|
side: "left"
|
|
30570
30813
|
}
|
|
30571
30814
|
];
|
|
30815
|
+
const STATUSLINE_ITEM_KEYS = {
|
|
30816
|
+
model: {
|
|
30817
|
+
label: "statusline.item.model",
|
|
30818
|
+
description: "statusline.item.model.desc"
|
|
30819
|
+
},
|
|
30820
|
+
cwd: {
|
|
30821
|
+
label: "statusline.item.cwd",
|
|
30822
|
+
description: "statusline.item.cwd.desc"
|
|
30823
|
+
},
|
|
30824
|
+
mode: {
|
|
30825
|
+
label: "statusline.item.mode",
|
|
30826
|
+
description: "statusline.item.mode.desc"
|
|
30827
|
+
},
|
|
30828
|
+
branch: {
|
|
30829
|
+
label: "statusline.item.branch",
|
|
30830
|
+
description: "statusline.item.branch.desc"
|
|
30831
|
+
},
|
|
30832
|
+
context: {
|
|
30833
|
+
label: "statusline.item.context",
|
|
30834
|
+
description: "statusline.item.context.desc"
|
|
30835
|
+
},
|
|
30836
|
+
permission: {
|
|
30837
|
+
label: "statusline.item.permission",
|
|
30838
|
+
description: "statusline.item.permission.desc"
|
|
30839
|
+
},
|
|
30840
|
+
plan: {
|
|
30841
|
+
label: "statusline.item.plan",
|
|
30842
|
+
description: "statusline.item.plan.desc"
|
|
30843
|
+
},
|
|
30844
|
+
turns: {
|
|
30845
|
+
label: "statusline.item.turns",
|
|
30846
|
+
description: "statusline.item.turns.desc"
|
|
30847
|
+
},
|
|
30848
|
+
durations: {
|
|
30849
|
+
label: "statusline.item.durations",
|
|
30850
|
+
description: "statusline.item.durations.desc"
|
|
30851
|
+
},
|
|
30852
|
+
cache: {
|
|
30853
|
+
label: "statusline.item.cache",
|
|
30854
|
+
description: "statusline.item.cache.desc"
|
|
30855
|
+
},
|
|
30856
|
+
tokens: {
|
|
30857
|
+
label: "statusline.item.tokens",
|
|
30858
|
+
description: "statusline.item.tokens.desc"
|
|
30859
|
+
},
|
|
30860
|
+
title: {
|
|
30861
|
+
label: "statusline.item.title",
|
|
30862
|
+
description: "statusline.item.title.desc"
|
|
30863
|
+
},
|
|
30864
|
+
goal: {
|
|
30865
|
+
label: "statusline.item.goal",
|
|
30866
|
+
description: "statusline.item.goal.desc"
|
|
30867
|
+
},
|
|
30868
|
+
sandbox: {
|
|
30869
|
+
label: "statusline.item.sandbox",
|
|
30870
|
+
description: "statusline.item.sandbox.desc"
|
|
30871
|
+
}
|
|
30872
|
+
};
|
|
30873
|
+
/** Picker rows with labels/descriptions in the active interface language. */
|
|
30874
|
+
function localizedStatusItems() {
|
|
30875
|
+
return STATUS_ITEMS.map((item) => {
|
|
30876
|
+
const keys = STATUSLINE_ITEM_KEYS[item.id];
|
|
30877
|
+
return {
|
|
30878
|
+
...item,
|
|
30879
|
+
label: t(keys.label),
|
|
30880
|
+
description: t(keys.description)
|
|
30881
|
+
};
|
|
30882
|
+
});
|
|
30883
|
+
}
|
|
30572
30884
|
/**
|
|
30573
30885
|
* Default order: the whole catalog (matches the pre-customization bar).
|
|
30574
30886
|
* The busy dot is not an item — it always leads the identity cluster.
|
|
@@ -30597,9 +30909,10 @@ const WIDTH_SAFETY = 1;
|
|
|
30597
30909
|
/** Column budget for the session title before it ellipsizes. */
|
|
30598
30910
|
const TITLE_BUDGET = 48;
|
|
30599
30911
|
/**
|
|
30600
|
-
* Primary-row drop ranks:
|
|
30601
|
-
*
|
|
30602
|
-
*
|
|
30912
|
+
* Primary-row drop ranks: identity never drops; permission outranks context.
|
|
30913
|
+
* The drop ladder peels hint and trailing identity facts before removing
|
|
30914
|
+
* the context meter, so occupancy stays visible on a typical 120-column
|
|
30915
|
+
* terminal. Secondary-row groups reuse the remaining ranks independently.
|
|
30603
30916
|
*/
|
|
30604
30917
|
const RANK_TITLE = 10;
|
|
30605
30918
|
const RANK_TOKENS = 50;
|
|
@@ -30637,6 +30950,23 @@ function sep() {
|
|
|
30637
30950
|
tone: "label"
|
|
30638
30951
|
};
|
|
30639
30952
|
}
|
|
30953
|
+
/** True for the dim ` · ` that joins identity facts. */
|
|
30954
|
+
function isIdentitySep(span) {
|
|
30955
|
+
return span.text === " · " && span.tone === "label";
|
|
30956
|
+
}
|
|
30957
|
+
/**
|
|
30958
|
+
* Drop the trailing identity fact (branch, then mode, then cwd) so the
|
|
30959
|
+
* context meter can keep a column. Returns undefined once only the busy
|
|
30960
|
+
* dot and model remain.
|
|
30961
|
+
*/
|
|
30962
|
+
function peelIdentityFact(spans) {
|
|
30963
|
+
if (spans.length < 3) return void 0;
|
|
30964
|
+
const last = spans[spans.length - 1];
|
|
30965
|
+
const before = spans[spans.length - 2];
|
|
30966
|
+
const modeSep = spans[spans.length - 3];
|
|
30967
|
+
if (last?.tone === "accent" && modeSep !== void 0 && isIdentitySep(modeSep) && before?.tone === "label") return spans.slice(0, -3);
|
|
30968
|
+
if ((last?.tone === "branch" || last?.tone === "path") && before !== void 0 && isIdentitySep(before)) return spans.slice(0, -2);
|
|
30969
|
+
}
|
|
30640
30970
|
/** Total visible columns of a span list (separators ride inside the spans). */
|
|
30641
30971
|
function spansWidth(spans) {
|
|
30642
30972
|
let width = 0;
|
|
@@ -30805,7 +31135,10 @@ function buildCandidates(facts, stats, busy, enabled, contextWidth) {
|
|
|
30805
31135
|
});
|
|
30806
31136
|
if (facts.goal !== void 0 && enabled.has("goal")) row2.push({
|
|
30807
31137
|
group: { spans: [{
|
|
30808
|
-
text: facts.goal.phase === "active" ? "
|
|
31138
|
+
text: facts.goal.phase === "active" ? t("status.goal.round", {
|
|
31139
|
+
current: facts.goal.rounds,
|
|
31140
|
+
max: facts.goal.max
|
|
31141
|
+
}) : t("status.goal.phase", { phase: safe(facts.goal.phase) }),
|
|
30809
31142
|
tone: "accent"
|
|
30810
31143
|
}] },
|
|
30811
31144
|
rank: RANK_GOAL,
|
|
@@ -30814,7 +31147,7 @@ function buildCandidates(facts, stats, busy, enabled, contextWidth) {
|
|
|
30814
31147
|
const sandbox = safe(facts.sandbox ?? "");
|
|
30815
31148
|
if (sandbox !== "" && sandbox.toLowerCase() !== facts.permission.toLowerCase() && enabled.has("sandbox")) row2.push({
|
|
30816
31149
|
group: { spans: [{
|
|
30817
|
-
text: "sandbox " + sandbox,
|
|
31150
|
+
text: t("status.label.sandbox") + " " + sandbox,
|
|
30818
31151
|
tone: "warn"
|
|
30819
31152
|
}] },
|
|
30820
31153
|
rank: RANK_SANDBOX,
|
|
@@ -30826,7 +31159,7 @@ function buildCandidates(facts, stats, busy, enabled, contextWidth) {
|
|
|
30826
31159
|
if (permission !== "" && enabled.has("permission")) {
|
|
30827
31160
|
right.push({
|
|
30828
31161
|
span: planStation ? {
|
|
30829
|
-
text: "plan
|
|
31162
|
+
text: t("status.plan.on"),
|
|
30830
31163
|
tone: "plan"
|
|
30831
31164
|
} : {
|
|
30832
31165
|
text: permission,
|
|
@@ -30839,7 +31172,7 @@ function buildCandidates(facts, stats, busy, enabled, contextWidth) {
|
|
|
30839
31172
|
}
|
|
30840
31173
|
if (facts.plan && enabled.has("plan")) row2.push({
|
|
30841
31174
|
group: { spans: [{
|
|
30842
|
-
text: "
|
|
31175
|
+
text: t("status.plan.mark"),
|
|
30843
31176
|
tone: "accent"
|
|
30844
31177
|
}] },
|
|
30845
31178
|
rank: RANK2_PLAN,
|
|
@@ -30855,7 +31188,8 @@ function buildCandidates(facts, stats, busy, enabled, contextWidth) {
|
|
|
30855
31188
|
/**
|
|
30856
31189
|
* Compose the two-row footer layout under a column budget. Row 1 keeps model,
|
|
30857
31190
|
* cwd, mode, branch, context, then the right-pinned permission badge and cycle
|
|
30858
|
-
* hint. It
|
|
31191
|
+
* hint. It shrinks the context bar, drops the hint, and peels trailing
|
|
31192
|
+
* identity facts before removing the context group or the permission badge.
|
|
30859
31193
|
* Row 2 fits all secondary figures and state within its own budget.
|
|
30860
31194
|
* @param facts - identity facts resolved by the runner.
|
|
30861
31195
|
* @param stats - session figures folded from the durable log.
|
|
@@ -30912,26 +31246,24 @@ function layoutStatusBar(facts, stats, columns, options = {}) {
|
|
|
30912
31246
|
rebuildContext();
|
|
30913
31247
|
continue;
|
|
30914
31248
|
}
|
|
30915
|
-
|
|
31249
|
+
}
|
|
31250
|
+
if (hint) {
|
|
31251
|
+
hint = false;
|
|
30916
31252
|
continue;
|
|
30917
31253
|
}
|
|
30918
|
-
|
|
30919
|
-
|
|
30920
|
-
const
|
|
30921
|
-
|
|
30922
|
-
if (identityBudget > 0 && visibleColumns(identityText) > identityBudget) {
|
|
31254
|
+
const identityEntry = leftKept[0];
|
|
31255
|
+
if (identityEntry?.id === "identity") {
|
|
31256
|
+
const peeled = peelIdentityFact(identityEntry.group.spans);
|
|
31257
|
+
if (peeled !== void 0) {
|
|
30923
31258
|
leftKept[0] = {
|
|
30924
|
-
...
|
|
30925
|
-
group: { spans:
|
|
30926
|
-
text: truncateColumns(identityText, identityBudget),
|
|
30927
|
-
tone: "model"
|
|
30928
|
-
}] }
|
|
31259
|
+
...identityEntry,
|
|
31260
|
+
group: { spans: peeled }
|
|
30929
31261
|
};
|
|
30930
31262
|
continue;
|
|
30931
31263
|
}
|
|
30932
31264
|
}
|
|
30933
|
-
if (
|
|
30934
|
-
|
|
31265
|
+
if (leftKept.some((entry) => entry.id === "context")) {
|
|
31266
|
+
leftKept.splice(leftKept.findIndex((entry) => entry.id === "context"), 1);
|
|
30935
31267
|
continue;
|
|
30936
31268
|
}
|
|
30937
31269
|
let dropLeft = -1;
|
|
@@ -32092,7 +32424,7 @@ function ModePanel({ current, load, select, close }) {
|
|
|
32092
32424
|
const [cursor, setCursor] = (0, import_react.useState)(0);
|
|
32093
32425
|
const [loading, setLoading] = (0, import_react.useState)(true);
|
|
32094
32426
|
const [error, setError] = (0, import_react.useState)();
|
|
32095
|
-
const refresh = () => {
|
|
32427
|
+
const refresh = (0, import_react.useCallback)(() => {
|
|
32096
32428
|
setLoading(true);
|
|
32097
32429
|
setError(void 0);
|
|
32098
32430
|
Promise.resolve().then(load).then((value) => {
|
|
@@ -32102,8 +32434,8 @@ function ModePanel({ current, load, select, close }) {
|
|
|
32102
32434
|
setError(reason instanceof Error ? reason.message : String(reason));
|
|
32103
32435
|
setLoading(false);
|
|
32104
32436
|
});
|
|
32105
|
-
};
|
|
32106
|
-
(0, import_react.useEffect)(refresh, []);
|
|
32437
|
+
}, [load]);
|
|
32438
|
+
(0, import_react.useEffect)(refresh, [refresh]);
|
|
32107
32439
|
const visible = (0, import_react.useMemo)(() => rows.filter((row) => `${row.id} ${row.name ?? ""} ${row.description ?? ""}`.toLowerCase().includes(query.toLowerCase())), [rows, query]);
|
|
32108
32440
|
(0, import_react.useEffect)(() => setCursor((value) => Math.min(value, Math.max(0, visible.length - 1))), [visible.length]);
|
|
32109
32441
|
useInput((input, key) => {
|
|
@@ -32139,7 +32471,7 @@ function PermissionPanel({ current, load, select, close }) {
|
|
|
32139
32471
|
const [cursor, setCursor] = (0, import_react.useState)(0);
|
|
32140
32472
|
const [loading, setLoading] = (0, import_react.useState)(true);
|
|
32141
32473
|
const [error, setError] = (0, import_react.useState)();
|
|
32142
|
-
const refresh = () => {
|
|
32474
|
+
const refresh = (0, import_react.useCallback)(() => {
|
|
32143
32475
|
setLoading(true);
|
|
32144
32476
|
setError(void 0);
|
|
32145
32477
|
Promise.resolve().then(load).then((value) => {
|
|
@@ -32149,8 +32481,8 @@ function PermissionPanel({ current, load, select, close }) {
|
|
|
32149
32481
|
setError(reason instanceof Error ? reason.message : String(reason));
|
|
32150
32482
|
setLoading(false);
|
|
32151
32483
|
});
|
|
32152
|
-
};
|
|
32153
|
-
(0, import_react.useEffect)(refresh, []);
|
|
32484
|
+
}, [load]);
|
|
32485
|
+
(0, import_react.useEffect)(refresh, [refresh]);
|
|
32154
32486
|
const visible = (0, import_react.useMemo)(() => rows.filter((row) => `${row.id} ${row.description ?? ""}`.toLowerCase().includes(query.toLowerCase())), [rows, query]);
|
|
32155
32487
|
(0, import_react.useEffect)(() => setCursor((value) => Math.min(value, Math.max(0, visible.length - 1))), [visible.length]);
|
|
32156
32488
|
useInput((input, key) => {
|
|
@@ -32184,7 +32516,11 @@ function PluginPanel({ load, close, initialQuery = "" }) {
|
|
|
32184
32516
|
const [query, setQuery] = (0, import_react.useState)(initialQuery);
|
|
32185
32517
|
const [cursor, setCursor] = (0, import_react.useState)(0);
|
|
32186
32518
|
const [expanded, setExpanded] = (0, import_react.useState)(false);
|
|
32187
|
-
const rows = (0, import_react.useMemo)(() => load().filter((row) => `${row.entryId} ${row.moduleName} ${row.phase ?? ""}`.toLowerCase().includes(query.toLowerCase())), [
|
|
32519
|
+
const rows = (0, import_react.useMemo)(() => load().filter((row) => `${row.entryId} ${row.moduleName} ${row.phase ?? ""}`.toLowerCase().includes(query.toLowerCase())), [
|
|
32520
|
+
epoch,
|
|
32521
|
+
load,
|
|
32522
|
+
query
|
|
32523
|
+
]);
|
|
32188
32524
|
(0, import_react.useEffect)(() => setCursor((value) => Math.min(value, Math.max(0, rows.length - 1))), [rows.length]);
|
|
32189
32525
|
useInput((input, key) => {
|
|
32190
32526
|
if (key.escape) return close();
|
|
@@ -32263,10 +32599,10 @@ function JobsPanel({ load, close }) {
|
|
|
32263
32599
|
footer: t("panel.footer.inspectRefresh")
|
|
32264
32600
|
});
|
|
32265
32601
|
}
|
|
32266
|
-
function ResumePanel({ currentCwd, load, readTranscript, select, requestDelete, deleteConfirmId, reloadToken = 0, deleteMode = false, close }) {
|
|
32602
|
+
function ResumePanel({ currentCwd, load, readTranscript, select, requestDelete, deleteConfirmId, reloadToken = 0, deleteMode = false, presetId, close }) {
|
|
32267
32603
|
const [options, setOptions] = (0, import_react.useState)({
|
|
32268
|
-
sessions: "roots",
|
|
32269
|
-
cwd: "current",
|
|
32604
|
+
sessions: presetId === void 0 || presetId === "" ? "roots" : "all",
|
|
32605
|
+
cwd: presetId === void 0 || presetId === "" ? "current" : "all",
|
|
32270
32606
|
sort: "newest",
|
|
32271
32607
|
currentCwd,
|
|
32272
32608
|
query: ""
|
|
@@ -32301,8 +32637,30 @@ function ResumePanel({ currentCwd, load, readTranscript, select, requestDelete,
|
|
|
32301
32637
|
}
|
|
32302
32638
|
});
|
|
32303
32639
|
return () => controller.abort();
|
|
32304
|
-
}, [
|
|
32640
|
+
}, [
|
|
32641
|
+
load,
|
|
32642
|
+
options,
|
|
32643
|
+
reloadToken
|
|
32644
|
+
]);
|
|
32305
32645
|
(0, import_react.useEffect)(() => setCursor((value) => Math.min(value, Math.max(0, rows.length - 1))), [rows.length]);
|
|
32646
|
+
const presetArmed = (0, import_react.useRef)(false);
|
|
32647
|
+
(0, import_react.useEffect)(() => {
|
|
32648
|
+
if (presetArmed.current || presetId === void 0 || presetId === "" || rows.length === 0 || requestDelete === void 0) return;
|
|
32649
|
+
try {
|
|
32650
|
+
const row = matchSessionRow(rows, presetId);
|
|
32651
|
+
presetArmed.current = true;
|
|
32652
|
+
const index = rows.findIndex((candidate) => candidate.id === row.id);
|
|
32653
|
+
if (index >= 0) setCursor(index);
|
|
32654
|
+
requestDelete(row);
|
|
32655
|
+
} catch (reason) {
|
|
32656
|
+
presetArmed.current = true;
|
|
32657
|
+
setError(reason instanceof Error ? reason.message : String(reason));
|
|
32658
|
+
}
|
|
32659
|
+
}, [
|
|
32660
|
+
rows,
|
|
32661
|
+
presetId,
|
|
32662
|
+
requestDelete
|
|
32663
|
+
]);
|
|
32306
32664
|
const cycle = () => {
|
|
32307
32665
|
if (focus === 3) {
|
|
32308
32666
|
setDensity((current) => current === "comfortable" ? "dense" : "comfortable");
|
|
@@ -32361,7 +32719,6 @@ function ResumePanel({ currentCwd, load, readTranscript, select, requestDelete,
|
|
|
32361
32719
|
if (key.pageDown) return setCursor((value) => Math.min(rows.length - 1, value + 8));
|
|
32362
32720
|
if (input === "g") return setCursor(0);
|
|
32363
32721
|
if (input === "G") return setCursor(Math.max(0, rows.length - 1));
|
|
32364
|
-
if (input === "d" && rows[cursor] !== void 0 && requestDelete !== void 0) return requestDelete(rows[cursor]);
|
|
32365
32722
|
if (input === "e" && rows[cursor] !== void 0) return setExpanded((value) => value === rows[cursor].id ? void 0 : rows[cursor].id);
|
|
32366
32723
|
if (input === "t" && rows[cursor] !== void 0) {
|
|
32367
32724
|
const row = rows[cursor];
|
|
@@ -32382,7 +32739,13 @@ function ResumePanel({ currentCwd, load, readTranscript, select, requestDelete,
|
|
|
32382
32739
|
});
|
|
32383
32740
|
return;
|
|
32384
32741
|
}
|
|
32385
|
-
if (key.return && rows[cursor]
|
|
32742
|
+
if (key.return && rows[cursor] !== void 0) {
|
|
32743
|
+
if (deleteMode) {
|
|
32744
|
+
if (requestDelete !== void 0 && !rows[cursor].live) requestDelete(rows[cursor]);
|
|
32745
|
+
return;
|
|
32746
|
+
}
|
|
32747
|
+
if (rows[cursor].resumable) select(rows[cursor]);
|
|
32748
|
+
}
|
|
32386
32749
|
}, { isActive: transcript === void 0 });
|
|
32387
32750
|
if (transcript !== void 0) return (0, import_react.createElement)(DocumentPanel, {
|
|
32388
32751
|
title: t("panel.document.title", { id: transcript.id }),
|
|
@@ -32396,10 +32759,17 @@ function ResumePanel({ currentCwd, load, readTranscript, select, requestDelete,
|
|
|
32396
32759
|
const pendingRow = deleteConfirmId === void 0 ? void 0 : rows.find((row) => row.id === deleteConfirmId);
|
|
32397
32760
|
const toolbar = `[${focus === 0 ? ">" : ""}${options.sessions}] [${focus === 1 ? ">" : ""}${options.cwd} cwd] [${focus === 2 ? ">" : ""}${options.sort}] [${focus === 3 ? ">" : ""}${density}]`;
|
|
32398
32761
|
return (0, import_react.createElement)(ListFrame, {
|
|
32399
|
-
title: deleteConfirmId
|
|
32762
|
+
title: deleteConfirmId !== void 0 ? t("panel.resume.deleteTitle", { target: pendingRow === void 0 ? deleteConfirmId.slice(-12) : pendingRow.title ?? pendingRow.id }) : deleteMode ? t("panel.delete.title", {
|
|
32763
|
+
search: searching ? ` — ${t("panel.searching")}` : "",
|
|
32764
|
+
toolbar
|
|
32765
|
+
}) : t("panel.resume.title", {
|
|
32766
|
+
mode: "",
|
|
32767
|
+
search: searching ? ` — ${t("panel.searching")}` : "",
|
|
32768
|
+
toolbar
|
|
32769
|
+
}),
|
|
32400
32770
|
rows: rows.map((row) => ({
|
|
32401
32771
|
key: row.id,
|
|
32402
|
-
disabled: !row.resumable,
|
|
32772
|
+
disabled: deleteMode ? row.live : !row.resumable,
|
|
32403
32773
|
text: `${row.subagent ? "↳" : "○"} ${row.title ?? row.id.slice(-12)}${density === "comfortable" ? ` · ${formatRelativeTime(row.updatedAt ?? row.createdAt, now)} · ${row.workspace} · ${row.preset}` : ""}${row.live ? " · live" : ""}${expanded === row.id ? ` · ${row.id} · ${row.cwd}${row.parent === void 0 ? "" : ` · parent ${row.parent}`}` : ""}`
|
|
32404
32774
|
})),
|
|
32405
32775
|
cursor,
|
|
@@ -32407,7 +32777,7 @@ function ResumePanel({ currentCwd, load, readTranscript, select, requestDelete,
|
|
|
32407
32777
|
error,
|
|
32408
32778
|
query: options.query,
|
|
32409
32779
|
searching,
|
|
32410
|
-
footer: t("panel.footer.resume")
|
|
32780
|
+
footer: t(deleteMode ? "panel.footer.delete" : "panel.footer.resume")
|
|
32411
32781
|
});
|
|
32412
32782
|
}
|
|
32413
32783
|
function documentStyleProps(style) {
|
|
@@ -32886,7 +33256,7 @@ function StatuslinePanel({ enabled, change, close }) {
|
|
|
32886
33256
|
const bodyRows = Math.max(1, viewport.bodyRows - 1);
|
|
32887
33257
|
const offset = revealRow(0, cursor, order.length, bodyRows);
|
|
32888
33258
|
const visible = order.slice(offset, offset + bodyRows);
|
|
32889
|
-
const meta = new Map(
|
|
33259
|
+
const meta = new Map(localizedStatusItems().map((item) => [item.id, item]));
|
|
32890
33260
|
const accent = panelAccent("statusline", getPalette().dim, getPalette().brandBright);
|
|
32891
33261
|
return (0, import_react.createElement)(Box, {
|
|
32892
33262
|
width: viewport.outerColumns,
|
|
@@ -33002,7 +33372,7 @@ function AgentsPanel({ live, load, readTranscript, close }) {
|
|
|
33002
33372
|
const [transcript, setTranscript] = (0, import_react.useState)();
|
|
33003
33373
|
const transcriptLoad = (0, import_react.useRef)();
|
|
33004
33374
|
(0, import_react.useEffect)(() => () => transcriptLoad.current?.abort(), []);
|
|
33005
|
-
const refresh = () => {
|
|
33375
|
+
const refresh = (0, import_react.useCallback)(() => {
|
|
33006
33376
|
setLoading(true);
|
|
33007
33377
|
setError(void 0);
|
|
33008
33378
|
Promise.resolve().then(load).then((value) => {
|
|
@@ -33012,8 +33382,8 @@ function AgentsPanel({ live, load, readTranscript, close }) {
|
|
|
33012
33382
|
setError(reason instanceof Error ? reason.message : String(reason));
|
|
33013
33383
|
setLoading(false);
|
|
33014
33384
|
});
|
|
33015
|
-
};
|
|
33016
|
-
(0, import_react.useEffect)(refresh, []);
|
|
33385
|
+
}, [load]);
|
|
33386
|
+
(0, import_react.useEffect)(refresh, [refresh]);
|
|
33017
33387
|
const rows = (0, import_react.useMemo)(() => {
|
|
33018
33388
|
const seen = new Set(live.map((row) => row.id));
|
|
33019
33389
|
const feedRows = live.map((row) => ({
|
|
@@ -33104,7 +33474,7 @@ function SubagentPanel({ current, load, pick, inherit, close }) {
|
|
|
33104
33474
|
const [loading, setLoading] = (0, import_react.useState)(true);
|
|
33105
33475
|
const [cursor, setCursor] = (0, import_react.useState)(0);
|
|
33106
33476
|
const [effortFor, setEffortFor] = (0, import_react.useState)(void 0);
|
|
33107
|
-
const refresh = () => {
|
|
33477
|
+
const refresh = (0, import_react.useCallback)(() => {
|
|
33108
33478
|
setLoading(true);
|
|
33109
33479
|
setError(void 0);
|
|
33110
33480
|
Promise.resolve().then(load).then((value) => {
|
|
@@ -33114,8 +33484,8 @@ function SubagentPanel({ current, load, pick, inherit, close }) {
|
|
|
33114
33484
|
setError(reason instanceof Error ? reason.message : String(reason));
|
|
33115
33485
|
setLoading(false);
|
|
33116
33486
|
});
|
|
33117
|
-
};
|
|
33118
|
-
(0, import_react.useEffect)(refresh, []);
|
|
33487
|
+
}, [load]);
|
|
33488
|
+
(0, import_react.useEffect)(refresh, [refresh]);
|
|
33119
33489
|
const rows = (0, import_react.useMemo)(() => directory?.rows ?? [], [directory]);
|
|
33120
33490
|
(0, import_react.useEffect)(() => {
|
|
33121
33491
|
if (current === "" || rows.length === 0) return;
|
|
@@ -33373,6 +33743,19 @@ function beginRecall(entries, draft) {
|
|
|
33373
33743
|
};
|
|
33374
33744
|
}
|
|
33375
33745
|
/**
|
|
33746
|
+
* Join a panel-recalled entry onto the draft already in the composer: the
|
|
33747
|
+
* draft is extended, never replaced, so picking a history row cannot discard
|
|
33748
|
+
* work in progress. An empty draft takes the entry as-is; otherwise the entry
|
|
33749
|
+
* starts on its own line unless the draft already ends one.
|
|
33750
|
+
* @param draft - the composer text before the recall.
|
|
33751
|
+
* @param entry - the sanitized text of the accepted row.
|
|
33752
|
+
* @returns the text to place in the composer.
|
|
33753
|
+
*/
|
|
33754
|
+
function appendRecall(draft, entry) {
|
|
33755
|
+
if (draft === "") return entry;
|
|
33756
|
+
return draft.endsWith("\n") ? draft + entry : `${draft}\n${entry}`;
|
|
33757
|
+
}
|
|
33758
|
+
/**
|
|
33376
33759
|
* Move one entry older (Up, toward index +1 in the newest-first space). The
|
|
33377
33760
|
* first Up saves the current draft so Down past the newest can restore it
|
|
33378
33761
|
* (Claude-Code shell recall — the draft is never lost); the oldest entry
|
|
@@ -33814,14 +34197,17 @@ function ProviderAuthorizationPanel(props) {
|
|
|
33814
34197
|
props.cancel(props.row.key);
|
|
33815
34198
|
decline();
|
|
33816
34199
|
};
|
|
34200
|
+
const propsRef = (0, import_react.useRef)(props);
|
|
34201
|
+
propsRef.current = props;
|
|
34202
|
+
const rowKey = props.row.key;
|
|
33817
34203
|
(0, import_react.useEffect)(() => () => {
|
|
33818
34204
|
controllerRef.current?.abort();
|
|
33819
|
-
|
|
34205
|
+
propsRef.current.cancel(rowKey);
|
|
33820
34206
|
const reply = replyRef.current;
|
|
33821
34207
|
replyRef.current = void 0;
|
|
33822
34208
|
reply?.detach();
|
|
33823
34209
|
reply?.reject(new AuthorizationDeclinedError());
|
|
33824
|
-
}, [
|
|
34210
|
+
}, [rowKey]);
|
|
33825
34211
|
const start = (method) => {
|
|
33826
34212
|
setPhase("running");
|
|
33827
34213
|
setError(void 0);
|
|
@@ -34086,17 +34472,37 @@ function detectImageMediaType(data) {
|
|
|
34086
34472
|
function looksLikeImagePath(path) {
|
|
34087
34473
|
return IMAGE_EXTENSIONS.has(extname(path).toLowerCase());
|
|
34088
34474
|
}
|
|
34475
|
+
/** Strip one layer of ASCII or Unicode quotes and shell-escaped spaces. */
|
|
34476
|
+
function unwrapDroppedPath(token) {
|
|
34477
|
+
const trimmed = token.trim();
|
|
34478
|
+
return (/^[\u2018\u201C"'](.+)[\u2019\u201D"']$/u.exec(trimmed)?.[1] ?? trimmed).replace(/\\ /gu, " ");
|
|
34479
|
+
}
|
|
34480
|
+
/** Absolute/relative drop with a dotted leaf — including unquoted spaces. */
|
|
34481
|
+
function looksLikeFilesystemDrop(path) {
|
|
34482
|
+
if (path.startsWith("file://")) return true;
|
|
34483
|
+
if (!/^(?:\/|[A-Za-z]:[\\/]|\\\\|\.\.?\/)/u.test(path)) return false;
|
|
34484
|
+
return looksLikeImagePath(path) || /\.[A-Za-z0-9]{1,16}$/u.test(path);
|
|
34485
|
+
}
|
|
34089
34486
|
/**
|
|
34090
|
-
*
|
|
34091
|
-
*
|
|
34092
|
-
|
|
34093
|
-
|
|
34487
|
+
* A composer draft that is a filesystem path, not a slash command.
|
|
34488
|
+
* `/usage` stays a command; `/Users/foo.png` and `C:\temp\a.png` are drops.
|
|
34489
|
+
*/
|
|
34490
|
+
function looksLikePathDraft(value) {
|
|
34491
|
+
const path = unwrapDroppedPath(value);
|
|
34492
|
+
if (looksLikeFilesystemDrop(path)) return true;
|
|
34493
|
+
return /^\/(?:Users|home|tmp|var|etc|opt|mnt|Volumes)\//u.test(path);
|
|
34494
|
+
}
|
|
34495
|
+
/**
|
|
34496
|
+
* Parse a paste or file-drop into image and file paths: image-suffixed
|
|
34497
|
+
* tokens stay images, other path-shaped tokens ride as file attachments
|
|
34498
|
+
* (0.1.5 file blocks), and anything that is neither leaves both empty —
|
|
34499
|
+
* the caller then treats the paste as plain text.
|
|
34094
34500
|
*
|
|
34095
34501
|
* File tokens are held to an absolute-path-with-shape bar (drive/backslash
|
|
34096
|
-
* or a
|
|
34502
|
+
* or a dotted leaf after a separator): a dropped terminal path always
|
|
34097
34503
|
* carries one of those, while prose, slash commands, and option flags never
|
|
34098
|
-
* do. A POSIX absolute path without
|
|
34099
|
-
*
|
|
34504
|
+
* do. A POSIX absolute path without a dotted leaf falls through as text —
|
|
34505
|
+
* the @ mention route still attaches such files deliberately.
|
|
34100
34506
|
*/
|
|
34101
34507
|
function parsePastedAttachmentPaths(input) {
|
|
34102
34508
|
const text = input.trim();
|
|
@@ -34107,32 +34513,41 @@ function parsePastedAttachmentPaths(input) {
|
|
|
34107
34513
|
const images = [];
|
|
34108
34514
|
const files = [];
|
|
34109
34515
|
const looksLikeDroppedFile = (path) => /^[A-Za-z]:[\\/]/u.test(path) || /^\\\\/u.test(path) || /^\/|^\.\.?\//u.test(path) && /\.[A-Za-z0-9]{1,16}$/u.test(path);
|
|
34110
|
-
|
|
34111
|
-
|
|
34112
|
-
if (
|
|
34113
|
-
|
|
34114
|
-
|
|
34115
|
-
|
|
34116
|
-
path = fileURLToPath(path);
|
|
34117
|
-
} catch {
|
|
34118
|
-
return {
|
|
34119
|
-
images: [],
|
|
34120
|
-
files: []
|
|
34121
|
-
};
|
|
34122
|
-
}
|
|
34123
|
-
if (looksLikeImagePath(path)) images.push(path);
|
|
34124
|
-
else files.push(path);
|
|
34125
|
-
continue;
|
|
34516
|
+
const classify = (raw) => {
|
|
34517
|
+
let path = unwrapDroppedPath(raw);
|
|
34518
|
+
if (path.startsWith("file://")) try {
|
|
34519
|
+
path = fileURLToPath(path);
|
|
34520
|
+
} catch {
|
|
34521
|
+
return "reject";
|
|
34126
34522
|
}
|
|
34523
|
+
else if (/%[0-9A-Fa-f]{2}/u.test(path)) try {
|
|
34524
|
+
path = decodeURIComponent(path);
|
|
34525
|
+
} catch {}
|
|
34127
34526
|
if (looksLikeImagePath(path)) {
|
|
34128
34527
|
images.push(path);
|
|
34129
|
-
|
|
34528
|
+
return "image";
|
|
34529
|
+
}
|
|
34530
|
+
if (looksLikeDroppedFile(path) || looksLikeFilesystemDrop(path)) {
|
|
34531
|
+
files.push(path);
|
|
34532
|
+
return "file";
|
|
34130
34533
|
}
|
|
34131
|
-
|
|
34534
|
+
return "reject";
|
|
34535
|
+
};
|
|
34536
|
+
const whole = unwrapDroppedPath(text);
|
|
34537
|
+
if ((text.match(/(?:^|[\s"'])(?:\/|[A-Za-z]:[\\/]|\\\\|\.\.?\/|file:\/\/)/gu) ?? []).length <= 1 && looksLikeFilesystemDrop(whole) && (/\s/u.test(whole) || /^[\u2018\u201C"']/u.test(text))) {
|
|
34538
|
+
classify(whole);
|
|
34539
|
+
return {
|
|
34540
|
+
images,
|
|
34541
|
+
files
|
|
34542
|
+
};
|
|
34543
|
+
}
|
|
34544
|
+
for (const match of text.matchAll(/"([^"]+)"|'([^']+)'|(\S+)/gu)) {
|
|
34545
|
+
const token = match[1] ?? match[2] ?? match[3];
|
|
34546
|
+
if (token === void 0) continue;
|
|
34547
|
+
if (classify(token) === "reject") return {
|
|
34132
34548
|
images: [],
|
|
34133
34549
|
files: []
|
|
34134
34550
|
};
|
|
34135
|
-
files.push(path);
|
|
34136
34551
|
}
|
|
34137
34552
|
return {
|
|
34138
34553
|
images,
|
|
@@ -34301,6 +34716,12 @@ function readSettledRowCap() {
|
|
|
34301
34716
|
}
|
|
34302
34717
|
/** Reset region/style, clear the visible screen and scrollback, then home. */
|
|
34303
34718
|
const RESIZE_REFLOW_CLEAR = "\x1B[r\x1B[0m\x1B[H\x1B[2J\x1B[3J\x1B[H";
|
|
34719
|
+
/**
|
|
34720
|
+
* Same as {@link RESIZE_REFLOW_CLEAR} without wiping native scrollback.
|
|
34721
|
+
* History-cap trims remount `<Static>` but must not `\x1b[3J` a user who is
|
|
34722
|
+
* reading earlier messages above the fold.
|
|
34723
|
+
*/
|
|
34724
|
+
const TRIM_REFLOW_CLEAR = "\x1B[r\x1B[0m\x1B[H\x1B[2J\x1B[H";
|
|
34304
34725
|
/** Ask terminals supporting DEC synchronized updates to hold the frame. */
|
|
34305
34726
|
const SYNCHRONIZED_UPDATE_BEGIN = "\x1B[?2026h";
|
|
34306
34727
|
/** Release the held frame after Ink has replayed the source-backed Static rows. */
|
|
@@ -34440,6 +34861,35 @@ const LOCAL_COMMANDS = [
|
|
|
34440
34861
|
}
|
|
34441
34862
|
];
|
|
34442
34863
|
const LOCAL_COMMAND_NAMES = new Set(LOCAL_COMMANDS.map((command) => command.label.slice(1)));
|
|
34864
|
+
/**
|
|
34865
|
+
* TUI-local commands that take no input. Extra tokens used to fall through
|
|
34866
|
+
* as a prompt (`/queue clear` reached the model); they now surface usage.
|
|
34867
|
+
*/
|
|
34868
|
+
const BARE_LOCAL_COMMANDS = /* @__PURE__ */ new Set([
|
|
34869
|
+
"quit",
|
|
34870
|
+
"help",
|
|
34871
|
+
"clear",
|
|
34872
|
+
"copy",
|
|
34873
|
+
"update",
|
|
34874
|
+
"schedule",
|
|
34875
|
+
"statusline",
|
|
34876
|
+
"theme",
|
|
34877
|
+
"history",
|
|
34878
|
+
"queue",
|
|
34879
|
+
"usage",
|
|
34880
|
+
"agents",
|
|
34881
|
+
"todos",
|
|
34882
|
+
"subagent"
|
|
34883
|
+
]);
|
|
34884
|
+
/** Split a slash line into the command name and any trailing input. */
|
|
34885
|
+
function slashNameAndArgs(text) {
|
|
34886
|
+
const match = /^\/([a-z][a-z0-9_-]*)(?:$|[\t ](.*))$/u.exec(text);
|
|
34887
|
+
if (match === null || match[1] === void 0) return void 0;
|
|
34888
|
+
return {
|
|
34889
|
+
name: match[1],
|
|
34890
|
+
args: (match[2] ?? "").trim()
|
|
34891
|
+
};
|
|
34892
|
+
}
|
|
34443
34893
|
/** Pad text with spaces to a visible-column target (menu name column). */
|
|
34444
34894
|
function padColumns(text, width) {
|
|
34445
34895
|
const clipped = truncateColumns(singleLineText(text), width);
|
|
@@ -34559,12 +35009,20 @@ function DeepDivingLine({ since, animated = true }) {
|
|
|
34559
35009
|
* cap counts explicit newlines and terminal wrapping, slicing from the END so
|
|
34560
35010
|
* the freshest tokens stay visible while a long reply streams; the complete
|
|
34561
35011
|
* text lands in the flushed scrollback once the turn assembles it.
|
|
35012
|
+
*
|
|
35013
|
+
* Body wrap width for a streaming tail. `rowColumns` is the same width
|
|
35014
|
+
* passed to `transcriptEntryLines` (terminal minus the last-column safety);
|
|
35015
|
+
* the hanging prefix then shrinks the body so streamed text and settled
|
|
35016
|
+
* markdown wrap on the same column.
|
|
34562
35017
|
*/
|
|
34563
|
-
function
|
|
34564
|
-
const
|
|
34565
|
-
const safeRows = Math.max(1, maxRows);
|
|
35018
|
+
function streamTailBodyColumns(rowColumns, prefix, continuationPrefix = prefix) {
|
|
35019
|
+
const width = Math.max(1, Math.floor(rowColumns));
|
|
34566
35020
|
const prefixColumns = Math.max(visibleColumns(prefix), visibleColumns(continuationPrefix));
|
|
34567
|
-
|
|
35021
|
+
return Math.max(1, width - prefixColumns);
|
|
35022
|
+
}
|
|
35023
|
+
function StreamTail({ text, dim, maxRows, prefix = "", continuationPrefix = prefix, children, columns }) {
|
|
35024
|
+
const safeRows = Math.max(1, maxRows);
|
|
35025
|
+
const contentColumns = streamTailBodyColumns(columns, prefix, continuationPrefix);
|
|
34568
35026
|
const initial = displayTail(text, contentColumns, safeRows);
|
|
34569
35027
|
const tail = initial.truncated && safeRows > 1 ? displayTail(text, contentColumns, safeRows - 1) : initial;
|
|
34570
35028
|
const rows = tail.text.split("\n");
|
|
@@ -34823,9 +35281,9 @@ function Header({ resumed }) {
|
|
|
34823
35281
|
const version = dshKernelVersion();
|
|
34824
35282
|
return version === void 0 ? void 0 : `dsh-v${version}`;
|
|
34825
35283
|
})();
|
|
34826
|
-
const title =
|
|
35284
|
+
const title = headerBrandTitle();
|
|
34827
35285
|
const slogan = "Into the Unknown 探索未至之境";
|
|
34828
|
-
const hint = resumed ? "
|
|
35286
|
+
const hint = resumed ? t("header.hintResumed") : t("header.hint");
|
|
34829
35287
|
const copyWidths = [
|
|
34830
35288
|
visibleColumns(title),
|
|
34831
35289
|
visibleColumns(slogan),
|
|
@@ -35276,23 +35734,14 @@ function StatusLine({ facts, stats, busy, columns, items, onRows, animated }) {
|
|
|
35276
35734
|
const flowTick = useFrames(125, flowActive);
|
|
35277
35735
|
const flowMs = flowActive ? flowTick * 125 + (flow?.phaseMs ?? 0) : void 0;
|
|
35278
35736
|
const language = getLanguage();
|
|
35279
|
-
const layout = (0, import_react.useMemo)(() =>
|
|
35280
|
-
|
|
35281
|
-
|
|
35282
|
-
|
|
35283
|
-
|
|
35284
|
-
|
|
35285
|
-
|
|
35286
|
-
facts
|
|
35287
|
-
facts.branch,
|
|
35288
|
-
facts.sessionId,
|
|
35289
|
-
facts.title,
|
|
35290
|
-
facts.sandbox,
|
|
35291
|
-
facts.plan,
|
|
35292
|
-
facts.permission,
|
|
35293
|
-
facts.goal?.phase,
|
|
35294
|
-
facts.goal?.rounds,
|
|
35295
|
-
facts.goal?.max,
|
|
35737
|
+
const layout = (0, import_react.useMemo)(() => {
|
|
35738
|
+
return layoutStatusBar(facts, stats, Math.max(8, columns - 2), {
|
|
35739
|
+
busy,
|
|
35740
|
+
items,
|
|
35741
|
+
contextWidth: Math.max(5, columns - 6)
|
|
35742
|
+
});
|
|
35743
|
+
}, [
|
|
35744
|
+
facts,
|
|
35296
35745
|
stats,
|
|
35297
35746
|
busy,
|
|
35298
35747
|
columns,
|
|
@@ -35534,7 +35983,7 @@ function QuestionBar({ store, snapshot, locked }) {
|
|
|
35534
35983
|
setSubmitted(false);
|
|
35535
35984
|
}, [request]);
|
|
35536
35985
|
const question = pending?.request.questions[index];
|
|
35537
|
-
const options = question?.options ?? [];
|
|
35986
|
+
const options = (0, import_react.useMemo)(() => question?.options ?? [], [question]);
|
|
35538
35987
|
const isPlan = question?.intent?.kind === "plan-review";
|
|
35539
35988
|
const isMulti = question?.multiSelect === true;
|
|
35540
35989
|
const { cursor, selected, mode, custom, scroll, manualScroll, followCustomTail } = drafts[index] ?? initialQuestionDraft(question);
|
|
@@ -35844,7 +36293,7 @@ function ModelPanel({ directory, error, current, onSelect, onProviders, onRetry,
|
|
|
35844
36293
|
const [cursor, setCursor] = (0, import_react.useState)(0);
|
|
35845
36294
|
const stdout = useStdout().stdout;
|
|
35846
36295
|
const viewport = panelViewport(stdout?.columns ?? 80, stdout?.rows ?? 30);
|
|
35847
|
-
const rows = directory?.rows ?? [];
|
|
36296
|
+
const rows = (0, import_react.useMemo)(() => directory?.rows ?? [], [directory]);
|
|
35848
36297
|
const filtered = (0, import_react.useMemo)(() => {
|
|
35849
36298
|
if (query === "") return rows;
|
|
35850
36299
|
const needle = query.toLowerCase();
|
|
@@ -37171,9 +37620,8 @@ function entryKindLabel(entry) {
|
|
|
37171
37620
|
* retained entry is converted to physical rows, but only one viewport slice
|
|
37172
37621
|
* reaches Ink, so even a huge reasoning block cannot grow the dynamic tree.
|
|
37173
37622
|
*/
|
|
37174
|
-
function VerbosePanel({ entries, onClose }) {
|
|
37175
|
-
const
|
|
37176
|
-
const viewport = inspectorViewport(stdout?.columns ?? 80, stdout?.rows ?? 30);
|
|
37623
|
+
function VerbosePanel({ entries, onClose, columns, rows }) {
|
|
37624
|
+
const viewport = inspectorViewport(columns, rows);
|
|
37177
37625
|
const [cursor, setCursor] = (0, import_react.useState)(() => Math.max(0, entries.length - 1));
|
|
37178
37626
|
const [scroll, setScroll] = (0, import_react.useState)(0);
|
|
37179
37627
|
const savedScroll = (0, import_react.useRef)(/* @__PURE__ */ new Map());
|
|
@@ -37182,6 +37630,8 @@ function VerbosePanel({ entries, onClose }) {
|
|
|
37182
37630
|
const entry = entries[cursor];
|
|
37183
37631
|
const allLines = (0, import_react.useMemo)(() => entry === void 0 ? [] : transcriptEntryLines(entry, viewport.contentColumns), [entry, viewport.contentColumns]);
|
|
37184
37632
|
const visibleScroll = clampScroll(scroll, allLines.length, viewport.bodyRows);
|
|
37633
|
+
const visibleScrollRef = (0, import_react.useRef)(visibleScroll);
|
|
37634
|
+
visibleScrollRef.current = visibleScroll;
|
|
37185
37635
|
(0, import_react.useEffect)(() => {
|
|
37186
37636
|
cursorRef.current = cursor;
|
|
37187
37637
|
}, [cursor]);
|
|
@@ -37189,7 +37639,7 @@ function VerbosePanel({ entries, onClose }) {
|
|
|
37189
37639
|
const current = cursorRef.current;
|
|
37190
37640
|
const next = followInspectorCursor(current, previousLength.current, entries.length);
|
|
37191
37641
|
if (next !== current) {
|
|
37192
|
-
savedScroll.current.set(current,
|
|
37642
|
+
savedScroll.current.set(current, visibleScrollRef.current);
|
|
37193
37643
|
setCursor(next);
|
|
37194
37644
|
setScroll(savedScroll.current.get(next) ?? 0);
|
|
37195
37645
|
}
|
|
@@ -37282,6 +37732,15 @@ function StaticTranscript({ items }) {
|
|
|
37282
37732
|
}
|
|
37283
37733
|
const MemoStaticTranscript = (0, import_react.memo)(StaticTranscript);
|
|
37284
37734
|
/**
|
|
37735
|
+
* Wrap one completion-menu cursor step. An empty menu keeps the index at 0:
|
|
37736
|
+
* `% 0` is NaN, and a NaN index silently disables the highlight, the accept
|
|
37737
|
+
* key, and any later Enter that runs through the menu.
|
|
37738
|
+
*/
|
|
37739
|
+
function stepCompletionIndex(index, delta, count) {
|
|
37740
|
+
if (count <= 0) return 0;
|
|
37741
|
+
return ((index + delta) % count + count) % count;
|
|
37742
|
+
}
|
|
37743
|
+
/**
|
|
37285
37744
|
* Resolve completion candidates for the current input: TUI-local commands,
|
|
37286
37745
|
* the live registry descriptors, and user-invocable skills, filtered by the
|
|
37287
37746
|
* typed prefix. Command names win collisions (the dispatch tries the
|
|
@@ -37454,22 +37913,20 @@ function Input({ active, frozen, frozenHint, busy, descriptors, skills, dispatch
|
|
|
37454
37913
|
(0, import_react.useEffect)(() => {
|
|
37455
37914
|
if (historyFill === void 0) return;
|
|
37456
37915
|
const safe = sanitizeDraftText(historyFill.text);
|
|
37457
|
-
|
|
37458
|
-
|
|
37459
|
-
|
|
37460
|
-
|
|
37461
|
-
|
|
37462
|
-
|
|
37463
|
-
setValue(safe);
|
|
37464
|
-
setCursor(safe.length);
|
|
37916
|
+
const current = valueRef.current;
|
|
37917
|
+
const joined = appendRecall(current, safe);
|
|
37918
|
+
valueRef.current = joined;
|
|
37919
|
+
cursorRef.current = joined.length;
|
|
37920
|
+
setValue(joined);
|
|
37921
|
+
setCursor(joined.length);
|
|
37465
37922
|
resetCursorBlink();
|
|
37466
37923
|
preferredColumnRef.current = null;
|
|
37467
37924
|
setDismissedMenuValue(void 0);
|
|
37468
37925
|
recall.current = {
|
|
37469
37926
|
entries: recallSpace,
|
|
37470
37927
|
index: historyFill.index,
|
|
37471
|
-
savedDraft:
|
|
37472
|
-
lastRecalled:
|
|
37928
|
+
savedDraft: current,
|
|
37929
|
+
lastRecalled: joined
|
|
37473
37930
|
};
|
|
37474
37931
|
historyConsumed();
|
|
37475
37932
|
}, [
|
|
@@ -37510,7 +37967,11 @@ function Input({ active, frozen, frozenHint, busy, descriptors, skills, dispatch
|
|
|
37510
37967
|
return () => {
|
|
37511
37968
|
stdin.read = originalRead;
|
|
37512
37969
|
};
|
|
37513
|
-
}, [
|
|
37970
|
+
}, [
|
|
37971
|
+
focusReporting,
|
|
37972
|
+
inputStdout,
|
|
37973
|
+
stdin
|
|
37974
|
+
]);
|
|
37514
37975
|
if (recall.current.entries !== recallSpace) {
|
|
37515
37976
|
const index = recall.current.index === null || recall.current.index < recallSpace.length ? recall.current.index : null;
|
|
37516
37977
|
recall.current = {
|
|
@@ -37522,7 +37983,7 @@ function Input({ active, frozen, frozenHint, busy, descriptors, skills, dispatch
|
|
|
37522
37983
|
const [completionIndex, setCompletionIndex] = (0, import_react.useState)(0);
|
|
37523
37984
|
const [dismissedMenuValue, setDismissedMenuValue] = (0, import_react.useState)(void 0);
|
|
37524
37985
|
const candidates = completionCandidates(value, descriptors, skills);
|
|
37525
|
-
const slashActive = candidates.length > 0 && value.startsWith("/") && !value.includes(" ") && !value.includes("\n");
|
|
37986
|
+
const slashActive = candidates.length > 0 && value.startsWith("/") && !value.includes(" ") && !value.includes("\n") && !looksLikePathDraft(value);
|
|
37526
37987
|
const beforeCursor = value.slice(0, cursor);
|
|
37527
37988
|
const lastLine = beforeCursor.split("\n").at(-1) ?? "";
|
|
37528
37989
|
const tokenMatch = /(^|\s)@([^\s]*)$/u.exec(lastLine);
|
|
@@ -37638,8 +38099,8 @@ function Input({ active, frozen, frozenHint, busy, descriptors, skills, dispatch
|
|
|
37638
38099
|
const requestId = mentionRequestRef.current + 1;
|
|
37639
38100
|
mentionRequestRef.current = requestId;
|
|
37640
38101
|
if (!active || !mentionActive) {
|
|
37641
|
-
setMentionRows([]);
|
|
37642
|
-
setMentionError(void 0);
|
|
38102
|
+
setMentionRows((current) => current.length === 0 ? current : []);
|
|
38103
|
+
setMentionError((current) => current === void 0 ? current : void 0);
|
|
37643
38104
|
return;
|
|
37644
38105
|
}
|
|
37645
38106
|
setMentionError(void 0);
|
|
@@ -37661,6 +38122,7 @@ function Input({ active, frozen, frozenHint, busy, descriptors, skills, dispatch
|
|
|
37661
38122
|
};
|
|
37662
38123
|
}, [
|
|
37663
38124
|
active,
|
|
38125
|
+
loadMentions,
|
|
37664
38126
|
mentionActive,
|
|
37665
38127
|
mentionToken?.query
|
|
37666
38128
|
]);
|
|
@@ -37681,6 +38143,22 @@ function Input({ active, frozen, frozenHint, busy, descriptors, skills, dispatch
|
|
|
37681
38143
|
origin: "mention"
|
|
37682
38144
|
})) : candidates;
|
|
37683
38145
|
const menuHeightRows = menuActive ? completionMenuRowCount(inputTerminalRows, menuRows.length) : 0;
|
|
38146
|
+
/**
|
|
38147
|
+
* What accepting the highlighted completion candidate would insert, or ''
|
|
38148
|
+
* when nothing can be accepted (no rows, or an image row that resolves
|
|
38149
|
+
* asynchronously). The Enter branch uses it to tell a real accept from a
|
|
38150
|
+
* no-op that must fall through to submission.
|
|
38151
|
+
*/
|
|
38152
|
+
const highlightedCompletion = () => {
|
|
38153
|
+
if (mentionActive) {
|
|
38154
|
+
const row = rankedMentionRows[completionIndex % Math.max(1, rankedMentionRows.length)];
|
|
38155
|
+
if (row === void 0) return "";
|
|
38156
|
+
if (row.kind === "file" && row.path !== void 0 && looksLikeImagePath(row.path)) return "";
|
|
38157
|
+
return row.label.startsWith("@") ? row.label : `@${row.label}${row.kind === "directory" ? "/" : ""}`;
|
|
38158
|
+
}
|
|
38159
|
+
const candidate = candidates[completionIndex % Math.max(1, candidates.length)];
|
|
38160
|
+
return candidate === void 0 ? "" : `${candidate.label} `;
|
|
38161
|
+
};
|
|
37684
38162
|
/** Accept the highlighted completion-menu candidate into the draft. */
|
|
37685
38163
|
const acceptMenuCandidate = () => {
|
|
37686
38164
|
if (mentionActive && mentionToken !== void 0) {
|
|
@@ -37768,6 +38246,14 @@ function Input({ active, frozen, frozenHint, busy, descriptors, skills, dispatch
|
|
|
37768
38246
|
preferredColumnRef.current = null;
|
|
37769
38247
|
setCompletionIndex(0);
|
|
37770
38248
|
setDismissedMenuValue(void 0);
|
|
38249
|
+
const dropped = parsePastedAttachmentPaths(edit.value);
|
|
38250
|
+
if (dropped.images.length > 0 || dropped.files.length > 0) {
|
|
38251
|
+
valueRef.current = "";
|
|
38252
|
+
cursorRef.current = 0;
|
|
38253
|
+
setValue("");
|
|
38254
|
+
setCursor(0);
|
|
38255
|
+
insertDroppedAttachments(dropped.images, dropped.files);
|
|
38256
|
+
}
|
|
37771
38257
|
};
|
|
37772
38258
|
/** Move the cursor without editing; horizontal moves clear the column preference. */
|
|
37773
38259
|
const moveCursorTo = (next) => {
|
|
@@ -37934,7 +38420,10 @@ function Input({ active, frozen, frozenHint, busy, descriptors, skills, dispatch
|
|
|
37934
38420
|
return;
|
|
37935
38421
|
}
|
|
37936
38422
|
if (menuActive) {
|
|
37937
|
-
|
|
38423
|
+
const highlighted = highlightedCompletion();
|
|
38424
|
+
const repeatsToken = mentionActive && mentionToken !== void 0 && cursor === liveValue.length && liveValue.slice(mentionToken.start, cursor) === highlighted;
|
|
38425
|
+
const exactSlash = !mentionActive && candidates.some((candidate) => candidate.label === liveValue);
|
|
38426
|
+
if (!(highlighted === "" || repeatsToken || exactSlash)) {
|
|
37938
38427
|
acceptMenuCandidate();
|
|
37939
38428
|
return;
|
|
37940
38429
|
}
|
|
@@ -38020,7 +38509,7 @@ function Input({ active, frozen, frozenHint, busy, descriptors, skills, dispatch
|
|
|
38020
38509
|
return;
|
|
38021
38510
|
}
|
|
38022
38511
|
if (text === "/copy") {
|
|
38023
|
-
copyLastResponse().then((outcome) => notify(outcome), (error) => notify(
|
|
38512
|
+
copyLastResponse().then((outcome) => notify(outcome), (error) => notify(t("notice.copyFailed", { message: error instanceof Error ? error.message : String(error) }), "error"));
|
|
38024
38513
|
return;
|
|
38025
38514
|
}
|
|
38026
38515
|
if (text === "/diff" || text.startsWith("/diff ")) {
|
|
@@ -38161,6 +38650,11 @@ function Input({ active, frozen, frozenHint, busy, descriptors, skills, dispatch
|
|
|
38161
38650
|
openDelete(text.slice(7).trim());
|
|
38162
38651
|
return;
|
|
38163
38652
|
}
|
|
38653
|
+
const slash = slashNameAndArgs(text);
|
|
38654
|
+
if (slash !== void 0 && BARE_LOCAL_COMMANDS.has(slash.name) && slash.args !== "") {
|
|
38655
|
+
notify(t("notice.usage.bareCommand", { name: slash.name }), "warning");
|
|
38656
|
+
return;
|
|
38657
|
+
}
|
|
38164
38658
|
if (submitMode === "steer") steer(text);
|
|
38165
38659
|
else dispatch(text);
|
|
38166
38660
|
return;
|
|
@@ -38176,11 +38670,11 @@ function Input({ active, frozen, frozenHint, busy, descriptors, skills, dispatch
|
|
|
38176
38670
|
return;
|
|
38177
38671
|
}
|
|
38178
38672
|
if (menuActive && key.upArrow) {
|
|
38179
|
-
setCompletionIndex((index) => (index
|
|
38673
|
+
setCompletionIndex((index) => stepCompletionIndex(index, -1, menuRows.length));
|
|
38180
38674
|
return;
|
|
38181
38675
|
}
|
|
38182
38676
|
if (menuActive && key.downArrow) {
|
|
38183
|
-
setCompletionIndex((index) => (index
|
|
38677
|
+
setCompletionIndex((index) => stepCompletionIndex(index, 1, menuRows.length));
|
|
38184
38678
|
return;
|
|
38185
38679
|
}
|
|
38186
38680
|
const rawTokens = rawEditorTokens.current;
|
|
@@ -38270,13 +38764,12 @@ function Input({ active, frozen, frozenHint, busy, descriptors, skills, dispatch
|
|
|
38270
38764
|
clearTimeout(timer);
|
|
38271
38765
|
pasteBracketCancelRef.current = void 0;
|
|
38272
38766
|
};
|
|
38273
|
-
text = text.replaceAll(PASTE_START_MARKER, "");
|
|
38274
38767
|
}
|
|
38275
38768
|
if (text.includes("[201~")) {
|
|
38276
38769
|
pasteBracketRef.current = false;
|
|
38277
38770
|
pasteBracketCancelRef.current?.();
|
|
38278
|
-
text = text.replaceAll(PASTE_END_MARKER, "");
|
|
38279
38771
|
}
|
|
38772
|
+
text = stripPasteMarkers(text);
|
|
38280
38773
|
if (text === "") return;
|
|
38281
38774
|
if (text.length > 1) {
|
|
38282
38775
|
const dropped = parsePastedAttachmentPaths(text);
|
|
@@ -38333,7 +38826,9 @@ function Input({ active, frozen, frozenHint, busy, descriptors, skills, dispatch
|
|
|
38333
38826
|
}), 2 + caret.column);
|
|
38334
38827
|
(0, import_react.useEffect)(() => {
|
|
38335
38828
|
onMenuRows(menuHeightRows);
|
|
38336
|
-
return () =>
|
|
38829
|
+
return () => {
|
|
38830
|
+
if (menuHeightRows !== 0) onMenuRows(0);
|
|
38831
|
+
};
|
|
38337
38832
|
}, [menuHeightRows, onMenuRows]);
|
|
38338
38833
|
const bandWidth = Math.max(1, columns - 1);
|
|
38339
38834
|
const bandBg = inkColor(getPalette().composerBand);
|
|
@@ -38665,6 +39160,8 @@ function App(props) {
|
|
|
38665
39160
|
const [authorizationDirectory, setAuthorizationDirectory] = (0, import_react.useState)(void 0);
|
|
38666
39161
|
const [authorizationError, setAuthorizationError] = (0, import_react.useState)(void 0);
|
|
38667
39162
|
const [modelLoadEpoch, setModelLoadEpoch] = (0, import_react.useState)(0);
|
|
39163
|
+
/** Bumped when /effort's catalog lookup must be ignored (panel closed or superseded). */
|
|
39164
|
+
const effortLookupEpoch = (0, import_react.useRef)(0);
|
|
38668
39165
|
const [notice, setNotice] = (0, import_react.useState)(void 0);
|
|
38669
39166
|
const notify = (0, import_react.useCallback)((text, tone = "info") => {
|
|
38670
39167
|
setNotice({
|
|
@@ -38672,15 +39169,16 @@ function App(props) {
|
|
|
38672
39169
|
tone
|
|
38673
39170
|
});
|
|
38674
39171
|
}, []);
|
|
39172
|
+
const { loadModels, loadModelProviders, loadProviderAuthorizations, onBridgeReady } = props;
|
|
38675
39173
|
(0, import_react.useEffect)(() => {
|
|
38676
|
-
|
|
38677
|
-
}, []);
|
|
39174
|
+
onBridgeReady({ notify });
|
|
39175
|
+
}, [notify, onBridgeReady]);
|
|
38678
39176
|
(0, import_react.useEffect)(() => {
|
|
38679
39177
|
if (!modelOpen) return;
|
|
38680
39178
|
let cancelled = false;
|
|
38681
39179
|
setDirectory(void 0);
|
|
38682
39180
|
setModelError(void 0);
|
|
38683
|
-
Promise.resolve().then(() =>
|
|
39181
|
+
Promise.resolve().then(() => loadModels()).then((loaded) => {
|
|
38684
39182
|
if (!cancelled) setDirectory(loaded);
|
|
38685
39183
|
}, (error) => {
|
|
38686
39184
|
if (!cancelled) setModelError(error instanceof Error ? error.message : String(error));
|
|
@@ -38689,16 +39187,16 @@ function App(props) {
|
|
|
38689
39187
|
cancelled = true;
|
|
38690
39188
|
};
|
|
38691
39189
|
}, [
|
|
39190
|
+
loadModels,
|
|
38692
39191
|
modelOpen,
|
|
38693
|
-
modelLoadEpoch
|
|
38694
|
-
props.loadModels
|
|
39192
|
+
modelLoadEpoch
|
|
38695
39193
|
]);
|
|
38696
39194
|
(0, import_react.useEffect)(() => {
|
|
38697
|
-
if (!modelOpen ||
|
|
39195
|
+
if (!modelOpen || loadModelProviders === void 0) return;
|
|
38698
39196
|
let cancelled = false;
|
|
38699
39197
|
setProviderDirectory(void 0);
|
|
38700
39198
|
setProviderError(void 0);
|
|
38701
|
-
Promise.resolve().then(() =>
|
|
39199
|
+
Promise.resolve().then(() => loadModelProviders()).then((loaded) => {
|
|
38702
39200
|
if (!cancelled) setProviderDirectory(loaded);
|
|
38703
39201
|
}, (error) => {
|
|
38704
39202
|
if (!cancelled) setProviderError(error instanceof Error ? error.message : String(error));
|
|
@@ -38707,16 +39205,16 @@ function App(props) {
|
|
|
38707
39205
|
cancelled = true;
|
|
38708
39206
|
};
|
|
38709
39207
|
}, [
|
|
39208
|
+
loadModelProviders,
|
|
38710
39209
|
modelOpen,
|
|
38711
|
-
modelLoadEpoch
|
|
38712
|
-
props.loadModelProviders
|
|
39210
|
+
modelLoadEpoch
|
|
38713
39211
|
]);
|
|
38714
39212
|
(0, import_react.useEffect)(() => {
|
|
38715
|
-
if (!modelOpen ||
|
|
39213
|
+
if (!modelOpen || loadProviderAuthorizations === void 0) return;
|
|
38716
39214
|
let cancelled = false;
|
|
38717
39215
|
setAuthorizationDirectory(void 0);
|
|
38718
39216
|
setAuthorizationError(void 0);
|
|
38719
|
-
Promise.resolve().then(() =>
|
|
39217
|
+
Promise.resolve().then(() => loadProviderAuthorizations()).then((loaded) => {
|
|
38720
39218
|
if (!cancelled) setAuthorizationDirectory(loaded);
|
|
38721
39219
|
}, (error) => {
|
|
38722
39220
|
if (!cancelled) setAuthorizationError(error instanceof Error ? error.message : String(error));
|
|
@@ -38725,9 +39223,9 @@ function App(props) {
|
|
|
38725
39223
|
cancelled = true;
|
|
38726
39224
|
};
|
|
38727
39225
|
}, [
|
|
39226
|
+
loadProviderAuthorizations,
|
|
38728
39227
|
modelOpen,
|
|
38729
|
-
modelLoadEpoch
|
|
38730
|
-
props.loadProviderAuthorizations
|
|
39228
|
+
modelLoadEpoch
|
|
38731
39229
|
]);
|
|
38732
39230
|
(0, import_react.useEffect)(() => {
|
|
38733
39231
|
const subscribe = props.subscribeModelProviders;
|
|
@@ -38770,6 +39268,8 @@ function App(props) {
|
|
|
38770
39268
|
const [pluginOpen, setPluginOpen] = (0, import_react.useState)(false);
|
|
38771
39269
|
const [pluginQuery, setPluginQuery] = (0, import_react.useState)("");
|
|
38772
39270
|
const [updateOpen, setUpdateOpen] = (0, import_react.useState)(false);
|
|
39271
|
+
const [updateApplying, setUpdateApplying] = (0, import_react.useState)(false);
|
|
39272
|
+
(0, import_react.useEffect)(() => subscribeUpdateApplyRunning(setUpdateApplying), []);
|
|
38773
39273
|
const [scheduleOpen, setScheduleOpen] = (0, import_react.useState)(false);
|
|
38774
39274
|
const [jobsOpen, setJobsOpen] = (0, import_react.useState)(false);
|
|
38775
39275
|
const [statuslineOpen, setStatuslineOpen] = (0, import_react.useState)(false);
|
|
@@ -38781,7 +39281,7 @@ function App(props) {
|
|
|
38781
39281
|
const [subagentOpen, setSubagentOpen] = (0, import_react.useState)(false);
|
|
38782
39282
|
const [todosOpen, setTodosOpen] = (0, import_react.useState)(false);
|
|
38783
39283
|
const [usageOpen, setUsageOpen] = (0, import_react.useState)(false);
|
|
38784
|
-
/** /delete state:
|
|
39284
|
+
/** /delete state: dedicated picker mode plus an optional pre-armed row id. */
|
|
38785
39285
|
const [resumeDelete, setResumeDelete] = (0, import_react.useState)({ mode: false });
|
|
38786
39286
|
/** The row id awaiting y/n in the COMPOSER (codex delete confirm): the
|
|
38787
39287
|
* composer takes the keys, the resume panel yields until it settles. */
|
|
@@ -38794,19 +39294,20 @@ function App(props) {
|
|
|
38794
39294
|
const cancelDelete = (0, import_react.useCallback)(() => {
|
|
38795
39295
|
setDeleteConfirmId(void 0);
|
|
38796
39296
|
}, []);
|
|
39297
|
+
const deleteSession = props.deleteSession;
|
|
38797
39298
|
const confirmDelete = (0, import_react.useCallback)(() => {
|
|
38798
39299
|
const id = deleteConfirmId;
|
|
38799
39300
|
if (id === void 0) return;
|
|
38800
39301
|
setDeleteConfirmId(void 0);
|
|
38801
|
-
|
|
39302
|
+
deleteSession(id).then((outcome) => {
|
|
38802
39303
|
notify(outcome);
|
|
38803
39304
|
setDeleteReloadToken((token) => token + 1);
|
|
38804
39305
|
}, (reason) => {
|
|
38805
39306
|
notify(t("notice.deleteFailed", { message: reason instanceof Error ? reason.message : String(reason) }), "error");
|
|
38806
39307
|
});
|
|
38807
39308
|
}, [
|
|
39309
|
+
deleteSession,
|
|
38808
39310
|
deleteConfirmId,
|
|
38809
|
-
props.deleteSession,
|
|
38810
39311
|
notify
|
|
38811
39312
|
]);
|
|
38812
39313
|
/** The /history panel's accepted entry: text plus its recall-space index. */
|
|
@@ -38841,31 +39342,137 @@ function App(props) {
|
|
|
38841
39342
|
const agentRows = (0, import_react.useSyncExternalStore)(subscribeSubagents, readAgentRows);
|
|
38842
39343
|
const approvalPending = approvalSnapshot.pending !== void 0;
|
|
38843
39344
|
const questionPending = questionSnapshot.pending !== void 0;
|
|
38844
|
-
|
|
38845
|
-
|
|
39345
|
+
/**
|
|
39346
|
+
* Every keyboard-owning surface that is mutually exclusive with the composer,
|
|
39347
|
+
* in precedence order. This ONE list drives the composer gate, the transcript
|
|
39348
|
+
* visibility, the frozen band's hint, and the hand-off when a human approval
|
|
39349
|
+
* or question arrives, so a panel cannot be wired into one of them and
|
|
39350
|
+
* forgotten in the others.
|
|
39351
|
+
*/
|
|
39352
|
+
const panelSurfaces = [
|
|
39353
|
+
{
|
|
39354
|
+
hint: "the diff review",
|
|
39355
|
+
open: diffView !== void 0,
|
|
39356
|
+
close: () => setDiffView(void 0)
|
|
39357
|
+
},
|
|
39358
|
+
{
|
|
39359
|
+
hint: "the review picker",
|
|
39360
|
+
open: reviewPickerOpen,
|
|
39361
|
+
close: () => setReviewPickerOpen(false)
|
|
39362
|
+
},
|
|
39363
|
+
{
|
|
39364
|
+
hint: "/model",
|
|
39365
|
+
open: modelOpen,
|
|
39366
|
+
close: () => {
|
|
39367
|
+
setModelOpen(false);
|
|
39368
|
+
setProviderOpen(false);
|
|
39369
|
+
setProviderAction(void 0);
|
|
39370
|
+
setEffortFor(void 0);
|
|
39371
|
+
}
|
|
39372
|
+
},
|
|
39373
|
+
{
|
|
39374
|
+
hint: "/help",
|
|
39375
|
+
open: helpOpen,
|
|
39376
|
+
close: () => setHelpOpen(false)
|
|
39377
|
+
},
|
|
39378
|
+
{
|
|
39379
|
+
hint: "/mode",
|
|
39380
|
+
open: modeOpen,
|
|
39381
|
+
close: () => setModeOpen(false)
|
|
39382
|
+
},
|
|
39383
|
+
{
|
|
39384
|
+
hint: "/permission",
|
|
39385
|
+
open: permissionOpen,
|
|
39386
|
+
close: () => setPermissionOpen(false)
|
|
39387
|
+
},
|
|
39388
|
+
{
|
|
39389
|
+
hint: "/resume",
|
|
39390
|
+
open: resumeOpen,
|
|
39391
|
+
close: () => setResumeOpen(false)
|
|
39392
|
+
},
|
|
39393
|
+
{
|
|
39394
|
+
hint: "/search",
|
|
39395
|
+
open: searchOpen,
|
|
39396
|
+
close: () => setSearchOpen(false)
|
|
39397
|
+
},
|
|
39398
|
+
{
|
|
39399
|
+
hint: "/plugin",
|
|
39400
|
+
open: pluginOpen,
|
|
39401
|
+
close: () => setPluginOpen(false)
|
|
39402
|
+
},
|
|
39403
|
+
{
|
|
39404
|
+
hint: "/update",
|
|
39405
|
+
open: updateOpen,
|
|
39406
|
+
close: () => setUpdateOpen(false)
|
|
39407
|
+
},
|
|
39408
|
+
{
|
|
39409
|
+
hint: "/schedule",
|
|
39410
|
+
open: scheduleOpen,
|
|
39411
|
+
close: () => setScheduleOpen(false)
|
|
39412
|
+
},
|
|
39413
|
+
{
|
|
39414
|
+
hint: "/jobs",
|
|
39415
|
+
open: jobsOpen,
|
|
39416
|
+
close: () => setJobsOpen(false)
|
|
39417
|
+
},
|
|
39418
|
+
{
|
|
39419
|
+
hint: "/statusline",
|
|
39420
|
+
open: statuslineOpen,
|
|
39421
|
+
close: () => setStatuslineOpen(false)
|
|
39422
|
+
},
|
|
39423
|
+
{
|
|
39424
|
+
hint: "/theme",
|
|
39425
|
+
open: themeOpen,
|
|
39426
|
+
close: () => setThemeOpen(false)
|
|
39427
|
+
},
|
|
39428
|
+
{
|
|
39429
|
+
hint: "/language",
|
|
39430
|
+
open: languageOpen,
|
|
39431
|
+
close: () => setLanguageOpen(false)
|
|
39432
|
+
},
|
|
39433
|
+
{
|
|
39434
|
+
hint: "/history",
|
|
39435
|
+
open: historyOpen,
|
|
39436
|
+
close: () => setHistoryOpen(false)
|
|
39437
|
+
},
|
|
39438
|
+
{
|
|
39439
|
+
hint: "/queue",
|
|
39440
|
+
open: queueOpen,
|
|
39441
|
+
close: () => setQueueOpen(false)
|
|
39442
|
+
},
|
|
39443
|
+
{
|
|
39444
|
+
hint: "/agents",
|
|
39445
|
+
open: agentsOpen,
|
|
39446
|
+
close: () => setAgentsOpen(false)
|
|
39447
|
+
},
|
|
39448
|
+
{
|
|
39449
|
+
hint: "/subagent",
|
|
39450
|
+
open: subagentOpen,
|
|
39451
|
+
close: () => setSubagentOpen(false)
|
|
39452
|
+
},
|
|
39453
|
+
{
|
|
39454
|
+
hint: "/todos",
|
|
39455
|
+
open: todosOpen,
|
|
39456
|
+
close: () => setTodosOpen(false)
|
|
39457
|
+
},
|
|
39458
|
+
{
|
|
39459
|
+
hint: "/usage",
|
|
39460
|
+
open: usageOpen,
|
|
39461
|
+
close: () => setUsageOpen(false)
|
|
39462
|
+
}
|
|
39463
|
+
];
|
|
39464
|
+
const panelSurfacesRef = (0, import_react.useRef)(panelSurfaces);
|
|
39465
|
+
panelSurfacesRef.current = panelSurfaces;
|
|
39466
|
+
const openPanel = panelSurfaces.find((surface) => surface.open);
|
|
39467
|
+
const inspectorVisible = verboseOpen && !approvalPending && !questionPending;
|
|
39468
|
+
const modalVisible = openPanel !== void 0 || inspectorVisible || approvalPending || questionPending;
|
|
39469
|
+
const inputActive = deleteConfirmId !== void 0 ? !approvalPending && !questionPending : !modalVisible;
|
|
39470
|
+
const transcriptVisible = !modalVisible;
|
|
38846
39471
|
(0, import_react.useEffect)(() => {
|
|
38847
39472
|
if (!approvalPending && !questionPending) return;
|
|
38848
|
-
|
|
38849
|
-
setProviderOpen(false);
|
|
38850
|
-
setProviderAction(void 0);
|
|
38851
|
-
setEffortFor(void 0);
|
|
38852
|
-
setHelpOpen(false);
|
|
38853
|
-
setModeOpen(false);
|
|
38854
|
-
setPermissionOpen(false);
|
|
38855
|
-
setResumeOpen(false);
|
|
38856
|
-
setPluginOpen(false);
|
|
38857
|
-
setUpdateOpen(false);
|
|
38858
|
-
setScheduleOpen(false);
|
|
38859
|
-
setStatuslineOpen(false);
|
|
38860
|
-
setThemeOpen(false);
|
|
38861
|
-
setHistoryOpen(false);
|
|
38862
|
-
setQueueOpen(false);
|
|
38863
|
-
setAgentsOpen(false);
|
|
38864
|
-
setSubagentOpen(false);
|
|
38865
|
-
setTodosOpen(false);
|
|
39473
|
+
for (const surface of panelSurfacesRef.current) if (surface.open) surface.close();
|
|
38866
39474
|
setDeleteConfirmId(void 0);
|
|
38867
39475
|
setVerboseOpen(false);
|
|
38868
|
-
setDiffView(void 0);
|
|
38869
39476
|
}, [approvalPending, questionPending]);
|
|
38870
39477
|
const appStdout = useStdout().stdout;
|
|
38871
39478
|
const [terminalSize, setTerminalSize] = (0, import_react.useState)(() => ({
|
|
@@ -38975,20 +39582,28 @@ function App(props) {
|
|
|
38975
39582
|
const auditedLiveLines = liveAudit.allocation.live === visibleLiveLines.length ? visibleLiveLines : visibleLiveLines.slice(-liveAudit.allocation.live);
|
|
38976
39583
|
const auditedReasoningRows = liveAudit.allocation.reasoning;
|
|
38977
39584
|
const auditedAnswerRows = liveAudit.allocation.answer;
|
|
38978
|
-
const
|
|
38979
|
-
|
|
38980
|
-
|
|
38981
|
-
|
|
39585
|
+
const keyboardOwner = approvalPending ? "the approval prompt" : questionPending ? "the question" : openPanel?.hint ?? (inspectorVisible ? "history details" : void 0);
|
|
39586
|
+
useStableInput((input, key) => {
|
|
39587
|
+
if (!(key.ctrl && input === "c")) return;
|
|
39588
|
+
if (approvalPending || questionPending || deleteConfirmId !== void 0) return;
|
|
39589
|
+
if (openPanel !== void 0) openPanel.close();
|
|
39590
|
+
else if (inspectorVisible) setVerboseOpen(false);
|
|
39591
|
+
}, true);
|
|
39592
|
+
const frozenHint = keyboardOwner === void 0 ? void 0 : t("frozen.keysGoTo", {
|
|
39593
|
+
owner: keyboardOwner,
|
|
39594
|
+
action: approvalPending ? t("frozen.action.rejects") : questionPending ? t("frozen.action.cancels") : updateApplying && updateOpen ? t("frozen.action.waits") : t("frozen.action.closes")
|
|
39595
|
+
});
|
|
38982
39596
|
const closeInspector = (0, import_react.useCallback)(() => {
|
|
38983
39597
|
setVerboseOpen(false);
|
|
38984
39598
|
}, []);
|
|
38985
|
-
const refreshScreen = () => {
|
|
39599
|
+
const refreshScreen = (0, import_react.useCallback)((opts) => {
|
|
39600
|
+
const clear = opts?.wipeScrollback === false ? TRIM_REFLOW_CLEAR : RESIZE_REFLOW_CLEAR;
|
|
38986
39601
|
if (appStdout !== void 0) {
|
|
38987
39602
|
synchronizedReplayPending.current = true;
|
|
38988
|
-
appStdout.write(
|
|
39603
|
+
appStdout.write(SYNCHRONIZED_UPDATE_BEGIN + clear);
|
|
38989
39604
|
}
|
|
38990
39605
|
setRefreshEpoch((epoch) => epoch + 1);
|
|
38991
|
-
};
|
|
39606
|
+
}, [appStdout]);
|
|
38992
39607
|
const applyRainbow = (seed) => {
|
|
38993
39608
|
rerollRainbow(seed);
|
|
38994
39609
|
setTheme("rainbow");
|
|
@@ -39005,10 +39620,11 @@ function App(props) {
|
|
|
39005
39620
|
const settledNeedsTrim = settledRowsCache.current?.needsTrim === true;
|
|
39006
39621
|
(0, import_react.useEffect)(() => {
|
|
39007
39622
|
if (!settledNeedsTrim || busy || streamingActive) return;
|
|
39008
|
-
refreshScreen();
|
|
39623
|
+
refreshScreen({ wipeScrollback: false });
|
|
39009
39624
|
}, [
|
|
39010
|
-
settledNeedsTrim,
|
|
39011
39625
|
busy,
|
|
39626
|
+
refreshScreen,
|
|
39627
|
+
settledNeedsTrim,
|
|
39012
39628
|
streamingActive
|
|
39013
39629
|
]);
|
|
39014
39630
|
const sessionHasImages = (0, import_react.useMemo)(() => view.entries.some((entry) => (entry.kind === "user" || entry.kind === "pending") && (entry.images?.length ?? 0) > 0), [view.entries]);
|
|
@@ -39033,6 +39649,7 @@ function App(props) {
|
|
|
39033
39649
|
setModelLoadEpoch((epoch) => epoch + 1);
|
|
39034
39650
|
};
|
|
39035
39651
|
const closeModelSurface = () => {
|
|
39652
|
+
effortLookupEpoch.current += 1;
|
|
39036
39653
|
setModelOpen(false);
|
|
39037
39654
|
setProviderOpen(false);
|
|
39038
39655
|
setProviderAction(void 0);
|
|
@@ -39244,7 +39861,8 @@ function App(props) {
|
|
|
39244
39861
|
prefix: "✻ ",
|
|
39245
39862
|
continuationPrefix: " ",
|
|
39246
39863
|
dim: true,
|
|
39247
|
-
maxRows: auditedReasoningRows
|
|
39864
|
+
maxRows: auditedReasoningRows,
|
|
39865
|
+
columns: Math.max(1, terminalColumns - 2)
|
|
39248
39866
|
}) : view.streaming === "" ? (0, import_react.createElement)(ShimmerLine, {
|
|
39249
39867
|
text: "✻ Thinking… (Ctrl/Alt+R to expand)",
|
|
39250
39868
|
animated: animations
|
|
@@ -39253,12 +39871,14 @@ function App(props) {
|
|
|
39253
39871
|
prefix: "✻ ",
|
|
39254
39872
|
continuationPrefix: " ",
|
|
39255
39873
|
dim: true,
|
|
39256
|
-
maxRows: auditedReasoningRows
|
|
39874
|
+
maxRows: auditedReasoningRows,
|
|
39875
|
+
columns: Math.max(1, terminalColumns - 2)
|
|
39257
39876
|
}) : void 0, view.streaming !== "" && auditedAnswerRows > 0 ? (0, import_react.createElement)(StreamTail, {
|
|
39258
39877
|
text: view.streaming,
|
|
39259
39878
|
dim: false,
|
|
39260
39879
|
maxRows: auditedAnswerRows,
|
|
39261
|
-
prefix: " "
|
|
39880
|
+
prefix: " ",
|
|
39881
|
+
columns: Math.max(1, terminalColumns - 2)
|
|
39262
39882
|
}, busy ? (0, import_react.createElement)(Caret, { animated: animations }) : void 0) : void 0, deepDivingVisible ? (0, import_react.createElement)(DeepDivingLine, {
|
|
39263
39883
|
since: view.busySince,
|
|
39264
39884
|
animated: animations
|
|
@@ -39310,7 +39930,9 @@ function App(props) {
|
|
|
39310
39930
|
close: () => setReviewPickerOpen(false)
|
|
39311
39931
|
}) : void 0, verboseOpen && !approvalPending && !questionPending ? (0, import_react.createElement)(MemoVerbosePanel, {
|
|
39312
39932
|
entries: view.entries,
|
|
39313
|
-
onClose: closeInspector
|
|
39933
|
+
onClose: closeInspector,
|
|
39934
|
+
columns: terminalColumns,
|
|
39935
|
+
rows: terminalRows
|
|
39314
39936
|
}) : void 0, modeOpen && !approvalPending && !questionPending ? (0, import_react.createElement)(ModePanel, {
|
|
39315
39937
|
current: props.mode,
|
|
39316
39938
|
load: props.loadPresets,
|
|
@@ -39338,11 +39960,13 @@ function App(props) {
|
|
|
39338
39960
|
currentCwd: props.workspaceRoot,
|
|
39339
39961
|
load: props.loadSessions,
|
|
39340
39962
|
readTranscript: props.loadSessionTranscript,
|
|
39341
|
-
requestDelete,
|
|
39963
|
+
requestDelete: resumeDelete.mode ? requestDelete : void 0,
|
|
39342
39964
|
deleteConfirmId,
|
|
39343
39965
|
reloadToken: deleteReloadToken,
|
|
39344
39966
|
deleteMode: resumeDelete.mode,
|
|
39967
|
+
presetId: resumeDelete.id,
|
|
39345
39968
|
select: (row) => {
|
|
39969
|
+
if (resumeDelete.mode) return;
|
|
39346
39970
|
props.switchSession(row);
|
|
39347
39971
|
setResumeOpen(false);
|
|
39348
39972
|
},
|
|
@@ -39467,6 +40091,7 @@ function App(props) {
|
|
|
39467
40091
|
interrupt: props.interrupt,
|
|
39468
40092
|
quit: props.quit,
|
|
39469
40093
|
openModel: () => {
|
|
40094
|
+
effortLookupEpoch.current += 1;
|
|
39470
40095
|
setDirectory(void 0);
|
|
39471
40096
|
setModelError(void 0);
|
|
39472
40097
|
setProviderDirectory(void 0);
|
|
@@ -39479,7 +40104,9 @@ function App(props) {
|
|
|
39479
40104
|
setModelOpen(true);
|
|
39480
40105
|
},
|
|
39481
40106
|
openEffort: () => {
|
|
40107
|
+
const epoch = ++effortLookupEpoch.current;
|
|
39482
40108
|
props.loadModels().then((loaded) => {
|
|
40109
|
+
if (epoch !== effortLookupEpoch.current) return;
|
|
39483
40110
|
const [provider, model] = modelLabel.split("/");
|
|
39484
40111
|
const row = loaded.rows.find((candidate) => candidate.provider === provider && candidate.model === model) ?? loaded.rows.find((candidate) => candidate.model === model && candidate.reasoning !== void 0) ?? loaded.rows.find((candidate) => candidate.model === model);
|
|
39485
40112
|
if (row === void 0) {
|
|
@@ -39542,7 +40169,7 @@ function App(props) {
|
|
|
39542
40169
|
mode: true,
|
|
39543
40170
|
...armed === void 0 ? {} : { id: armed }
|
|
39544
40171
|
});
|
|
39545
|
-
setDeleteConfirmId(
|
|
40172
|
+
setDeleteConfirmId(void 0);
|
|
39546
40173
|
setResumeOpen(true);
|
|
39547
40174
|
},
|
|
39548
40175
|
openDiff: (argument) => {
|
|
@@ -39765,7 +40392,23 @@ function createKeypressSplitter() {
|
|
|
39765
40392
|
}
|
|
39766
40393
|
const head = buffer[0];
|
|
39767
40394
|
if (head !== "\x1B") {
|
|
39768
|
-
|
|
40395
|
+
if (head < " " || head === "") {
|
|
40396
|
+
units.push(head);
|
|
40397
|
+
buffer = buffer.slice(1);
|
|
40398
|
+
continue;
|
|
40399
|
+
}
|
|
40400
|
+
let take = 0;
|
|
40401
|
+
while (take < buffer.length) {
|
|
40402
|
+
const ch = buffer[take];
|
|
40403
|
+
if (ch === "\x1B" || ch < " " || ch === "") break;
|
|
40404
|
+
if (ch >= "\ud800" && ch <= "\udbff") {
|
|
40405
|
+
if (buffer[take + 1] === void 0) break;
|
|
40406
|
+
take += 2;
|
|
40407
|
+
continue;
|
|
40408
|
+
}
|
|
40409
|
+
take += 1;
|
|
40410
|
+
}
|
|
40411
|
+
if (take === 0) break;
|
|
39769
40412
|
units.push(buffer.slice(0, take));
|
|
39770
40413
|
buffer = buffer.slice(take);
|
|
39771
40414
|
continue;
|
|
@@ -40457,8 +41100,23 @@ function createTranscriptStore(replay) {
|
|
|
40457
41100
|
}
|
|
40458
41101
|
},
|
|
40459
41102
|
reset() {
|
|
41103
|
+
const live = {
|
|
41104
|
+
streaming: acc.streaming,
|
|
41105
|
+
streamingReasoning: acc.streamingReasoning,
|
|
41106
|
+
busy: acc.busy,
|
|
41107
|
+
busySince: acc.busySince,
|
|
41108
|
+
model: acc.model,
|
|
41109
|
+
firstChunkAt: acc.firstChunkAt,
|
|
41110
|
+
stepStart: acc.stepStart
|
|
41111
|
+
};
|
|
40460
41112
|
acc = createReplayAccumulator();
|
|
40461
|
-
|
|
41113
|
+
acc.streaming = live.streaming;
|
|
41114
|
+
acc.streamingReasoning = live.streamingReasoning;
|
|
41115
|
+
acc.busy = live.busy;
|
|
41116
|
+
acc.busySince = live.busySince;
|
|
41117
|
+
acc.model = live.model;
|
|
41118
|
+
acc.firstChunkAt = live.firstChunkAt;
|
|
41119
|
+
acc.stepStart = live.stepStart;
|
|
40462
41120
|
dirty = true;
|
|
40463
41121
|
notify();
|
|
40464
41122
|
}
|
|
@@ -41183,7 +41841,12 @@ async function resolveEditorKeysStartupHint({ env, paths, flagPath }) {
|
|
|
41183
41841
|
function selectForkSeed(events, atSeq) {
|
|
41184
41842
|
if (atSeq !== void 0 && (!Number.isSafeInteger(atSeq) || atSeq < 0)) throw new Error("fork event sequence must be a non-negative integer");
|
|
41185
41843
|
const lastSeq = events.at(-1)?.seq ?? -1;
|
|
41186
|
-
|
|
41844
|
+
let boundary;
|
|
41845
|
+
if (atSeq === void 0 || atSeq > lastSeq) boundary = events.findLast((event) => event.type === "turn/end");
|
|
41846
|
+
else {
|
|
41847
|
+
const start = events.findLast((event) => event.type === "turn/start" && event.seq <= atSeq);
|
|
41848
|
+
boundary = start === void 0 ? void 0 : events.find((event) => event.type === "turn/end" && event.seq >= start.seq);
|
|
41849
|
+
}
|
|
41187
41850
|
if (boundary === void 0) throw new Error(atSeq !== void 0 && atSeq <= lastSeq ? `the turn containing event ${atSeq} has not completed` : "this session has no completed turn to fork from");
|
|
41188
41851
|
let cut = events.indexOf(boundary) + 1;
|
|
41189
41852
|
while (cut < events.length && events[cut]?.type !== "turn/start") cut += 1;
|
|
@@ -41199,6 +41862,7 @@ var SessionSwitchQueue = class {
|
|
|
41199
41862
|
failed;
|
|
41200
41863
|
pending;
|
|
41201
41864
|
pumping = false;
|
|
41865
|
+
running = false;
|
|
41202
41866
|
constructor(execute, failed) {
|
|
41203
41867
|
this.execute = execute;
|
|
41204
41868
|
this.failed = failed;
|
|
@@ -41219,6 +41883,15 @@ var SessionSwitchQueue = class {
|
|
|
41219
41883
|
this.pending = void 0;
|
|
41220
41884
|
return true;
|
|
41221
41885
|
}
|
|
41886
|
+
/**
|
|
41887
|
+
* Whether a queued change is being activated right now. Between the idle
|
|
41888
|
+
* wait and the handoff the old session is still installed, so a submission
|
|
41889
|
+
* made in that window would start a turn the handoff then discards; callers
|
|
41890
|
+
* use this to refuse one instead of losing it.
|
|
41891
|
+
*/
|
|
41892
|
+
get activating() {
|
|
41893
|
+
return this.running;
|
|
41894
|
+
}
|
|
41222
41895
|
async pump() {
|
|
41223
41896
|
this.pumping = true;
|
|
41224
41897
|
try {
|
|
@@ -41227,10 +41900,13 @@ var SessionSwitchQueue = class {
|
|
|
41227
41900
|
await observed.activity.whenIdle();
|
|
41228
41901
|
if (this.pending !== observed) continue;
|
|
41229
41902
|
this.pending = void 0;
|
|
41903
|
+
this.running = true;
|
|
41230
41904
|
try {
|
|
41231
41905
|
await this.execute(observed.value);
|
|
41232
41906
|
} catch (error) {
|
|
41233
41907
|
this.failed(error);
|
|
41908
|
+
} finally {
|
|
41909
|
+
this.running = false;
|
|
41234
41910
|
}
|
|
41235
41911
|
}
|
|
41236
41912
|
} finally {
|
|
@@ -42350,7 +43026,7 @@ async function run(ctx, startup, io) {
|
|
|
42350
43026
|
try {
|
|
42351
43027
|
parsed = currentMentions.parse(line);
|
|
42352
43028
|
} catch (error) {
|
|
42353
|
-
bridge.notify(
|
|
43029
|
+
bridge.notify(t("notice.invalidReference", { message: error instanceof Error ? error.message : String(error) }), "error");
|
|
42354
43030
|
return;
|
|
42355
43031
|
}
|
|
42356
43032
|
if (deliveryChain.epoch !== epoch) deliveryChain = {
|
|
@@ -42376,7 +43052,10 @@ async function run(ctx, startup, io) {
|
|
|
42376
43052
|
if (mode === "steer") currentAgent.steer(message);
|
|
42377
43053
|
else currentAgent.followup(message);
|
|
42378
43054
|
} catch (error) {
|
|
42379
|
-
bridge.notify(
|
|
43055
|
+
bridge.notify(t("notice.messageFailed", {
|
|
43056
|
+
kind: mode === "steer" ? "steering" : "message",
|
|
43057
|
+
message: error instanceof Error ? error.message : String(error)
|
|
43058
|
+
}), "error");
|
|
42380
43059
|
}
|
|
42381
43060
|
};
|
|
42382
43061
|
if (parsed.references.length === 0) {
|
|
@@ -42392,7 +43071,7 @@ async function run(ctx, startup, io) {
|
|
|
42392
43071
|
}, (error) => {
|
|
42393
43072
|
pendingControllers.delete(controller);
|
|
42394
43073
|
if (controller.signal.aborted || epoch !== atEpoch) return;
|
|
42395
|
-
bridge.notify(
|
|
43074
|
+
bridge.notify(t("notice.referenceFailed", { message: error instanceof Error ? error.message : String(error) }), "error");
|
|
42396
43075
|
});
|
|
42397
43076
|
});
|
|
42398
43077
|
};
|
|
@@ -42451,7 +43130,7 @@ async function run(ctx, startup, io) {
|
|
|
42451
43130
|
}
|
|
42452
43131
|
await next.handle.dispose().catch(() => {});
|
|
42453
43132
|
if (!quitting) renderCurrent();
|
|
42454
|
-
bridge.notify(
|
|
43133
|
+
bridge.notify(t("notice.activationFailed", { message: error instanceof Error ? error.message : String(error) }), "error");
|
|
42455
43134
|
return;
|
|
42456
43135
|
}
|
|
42457
43136
|
abortPendingControllers();
|
|
@@ -42467,23 +43146,27 @@ async function run(ctx, startup, io) {
|
|
|
42467
43146
|
}
|
|
42468
43147
|
}).catch((error) => {
|
|
42469
43148
|
pendingInputs.length = 0;
|
|
42470
|
-
bridge.notify(
|
|
43149
|
+
bridge.notify(t("notice.creationFailed", { message: error instanceof Error ? error.message : String(error) }), "error");
|
|
42471
43150
|
});
|
|
42472
43151
|
};
|
|
42473
43152
|
/** Deliver one readable line to the agent, expanding session mentions first. */
|
|
42474
43153
|
const sendNow = (text, images = [], mode = "followup") => {
|
|
42475
43154
|
const line = submissionPayload(text);
|
|
42476
43155
|
if (line.trim() === "" && images.length === 0) return;
|
|
43156
|
+
if (switchQueue.activating) {
|
|
43157
|
+
bridge.notify(t("notice.switchInProgress"), "warning");
|
|
43158
|
+
return;
|
|
43159
|
+
}
|
|
42477
43160
|
if (images.length === 0 && line.startsWith("/mode ")) {
|
|
42478
|
-
switchModeAction(line.slice(6).trim()).then((selected) => bridge.notify(
|
|
43161
|
+
switchModeAction(line.slice(6).trim()).then((selected) => bridge.notify(t("notice.modeChanged", { value: selected })), (error) => bridge.notify(t("notice.modeChangeFailed", { message: error instanceof Error ? error.message : String(error) }), "error"));
|
|
42479
43162
|
return;
|
|
42480
43163
|
}
|
|
42481
43164
|
if (images.length === 0 && line.startsWith("/permission ")) {
|
|
42482
43165
|
try {
|
|
42483
43166
|
const selected = setPermissionAction(line.slice(12).trim());
|
|
42484
|
-
bridge.notify(
|
|
43167
|
+
bridge.notify(t("notice.permissionChanged", { value: selected }));
|
|
42485
43168
|
} catch (error) {
|
|
42486
|
-
bridge.notify(
|
|
43169
|
+
bridge.notify(t("notice.permissionChangeFailed", { message: error instanceof Error ? error.message : String(error) }), "error");
|
|
42487
43170
|
}
|
|
42488
43171
|
return;
|
|
42489
43172
|
}
|
|
@@ -42533,7 +43216,7 @@ async function run(ctx, startup, io) {
|
|
|
42533
43216
|
bridge.notify(t(preserved > 0 ? "notice.turnCancelledKeepQueue" : "notice.turnCancelled"));
|
|
42534
43217
|
return true;
|
|
42535
43218
|
} catch (error) {
|
|
42536
|
-
bridge.notify(
|
|
43219
|
+
bridge.notify(t("notice.cancelFailed", { message: error instanceof Error ? error.message : String(error) }), "error");
|
|
42537
43220
|
return false;
|
|
42538
43221
|
}
|
|
42539
43222
|
};
|
|
@@ -42562,7 +43245,7 @@ async function run(ctx, startup, io) {
|
|
|
42562
43245
|
*/
|
|
42563
43246
|
const cycleMode = () => {
|
|
42564
43247
|
if (permissionPresets === void 0 || permissionPresets.names.length === 0) {
|
|
42565
|
-
bridge.notify("
|
|
43248
|
+
bridge.notify(t("notice.permissionPresetsUnmounted"), "warning");
|
|
42566
43249
|
return "";
|
|
42567
43250
|
}
|
|
42568
43251
|
try {
|
|
@@ -42605,7 +43288,7 @@ async function run(ctx, startup, io) {
|
|
|
42605
43288
|
selectPermission(permissionPresets, session, decision.preset);
|
|
42606
43289
|
return `plan → off · permission → ${decision.preset}`;
|
|
42607
43290
|
} catch (error) {
|
|
42608
|
-
bridge.notify(
|
|
43291
|
+
bridge.notify(t("notice.modeChangeFailed", { message: error instanceof Error ? error.message : String(error) }), "error");
|
|
42609
43292
|
return "";
|
|
42610
43293
|
}
|
|
42611
43294
|
};
|
|
@@ -42620,7 +43303,7 @@ async function run(ctx, startup, io) {
|
|
|
42620
43303
|
if (active === void 0) pendingSelection = selection;
|
|
42621
43304
|
else active.selection.picked = selection;
|
|
42622
43305
|
defaultModel.saveSelection(selection).catch((error) => {
|
|
42623
|
-
bridge.notify(
|
|
43306
|
+
bridge.notify(t("notice.modelNotDefault", { message: error instanceof Error ? error.message : String(error) }), "warning");
|
|
42624
43307
|
});
|
|
42625
43308
|
const llm = ctx.get("llm");
|
|
42626
43309
|
const resolveCallConfig = llm?.resolveCallConfig;
|
|
@@ -42629,7 +43312,7 @@ async function run(ctx, startup, io) {
|
|
|
42629
43312
|
model: selection.model,
|
|
42630
43313
|
...selection.reasoningEffort === void 0 ? {} : { reasoningEffort: selection.reasoningEffort }
|
|
42631
43314
|
})).catch((error) => {
|
|
42632
|
-
bridge.notify(
|
|
43315
|
+
bridge.notify(t("notice.modelSelectionRejected", { message: error instanceof Error ? error.message : String(error) }), "error");
|
|
42633
43316
|
});
|
|
42634
43317
|
return `${row.provider}/${row.model}`;
|
|
42635
43318
|
};
|
|
@@ -42653,7 +43336,7 @@ async function run(ctx, startup, io) {
|
|
|
42653
43336
|
*/
|
|
42654
43337
|
const exportTranscript = async (argument) => {
|
|
42655
43338
|
if (session === void 0) {
|
|
42656
|
-
bridge.notify("
|
|
43339
|
+
bridge.notify(t("notice.noSessionYet"), "warning");
|
|
42657
43340
|
return;
|
|
42658
43341
|
}
|
|
42659
43342
|
const wanted = argument.trim();
|
|
@@ -42663,9 +43346,9 @@ async function run(ctx, startup, io) {
|
|
|
42663
43346
|
const markdown = buildExportMarkdown(store.getView(), session.id);
|
|
42664
43347
|
try {
|
|
42665
43348
|
await writeFile(target, `${markdown}\n`, "utf8");
|
|
42666
|
-
bridge.notify(
|
|
43349
|
+
bridge.notify(t("notice.exported", { path: target }));
|
|
42667
43350
|
} catch (error) {
|
|
42668
|
-
bridge.notify(
|
|
43351
|
+
bridge.notify(t("notice.exportFailed", { message: error instanceof Error ? error.message : String(error) }), "error");
|
|
42669
43352
|
}
|
|
42670
43353
|
};
|
|
42671
43354
|
/**
|
|
@@ -42700,10 +43383,14 @@ async function run(ctx, startup, io) {
|
|
|
42700
43383
|
if (Number.isFinite(newest)) updated.set(record.header.id, newest);
|
|
42701
43384
|
} catch {}
|
|
42702
43385
|
}));
|
|
42703
|
-
const projected = projectSessionRows(records,
|
|
42704
|
-
|
|
43386
|
+
const projected = projectSessionRows(records, {
|
|
43387
|
+
...options,
|
|
43388
|
+
query: ""
|
|
43389
|
+
}, updated);
|
|
43390
|
+
const titleBudget = options.query.trim() === "" ? 32 : Math.min(projected.length, 128);
|
|
43391
|
+
const page = projected.slice(0, titleBudget);
|
|
42705
43392
|
if (page.length === 0) return projected;
|
|
42706
|
-
return mergeSessionTitles(projected, await sessionQuery.readTitleSnapshots(page.map((row) => row.id), signal));
|
|
43393
|
+
return mergeSessionTitles(projected, await sessionQuery.readTitleSnapshots(page.map((row) => row.id), signal)).filter((row) => sessionRowMatchesQuery(row, options.query));
|
|
42707
43394
|
};
|
|
42708
43395
|
/**
|
|
42709
43396
|
* Delete one session subtree (/delete, codex semantics: subagent threads go
|
|
@@ -42720,9 +43407,12 @@ async function run(ctx, startup, io) {
|
|
|
42720
43407
|
* Backends without a derivable artifact (non-JSONL) refuse the WHOLE
|
|
42721
43408
|
* deletion here — no file has been touched yet, so a backend or layout
|
|
42722
43409
|
* surprise can never strand a half-deleted subtree.
|
|
42723
|
-
* 3.
|
|
42724
|
-
*
|
|
42725
|
-
*
|
|
43410
|
+
* 3. Acquire every node's public persistence write handle before touching
|
|
43411
|
+
* files. The JSONL backend holds its cross-process kernel lease for each
|
|
43412
|
+
* handle, so another terminal's live session refuses the whole deletion.
|
|
43413
|
+
* 4. Artifacts are removed children-first while every lease remains held:
|
|
43414
|
+
* only an I/O error mid-delete can stop it short (reported with
|
|
43415
|
+
* removed/total counts), leaving the shallowest lineage intact.
|
|
42726
43416
|
*
|
|
42727
43417
|
* @param id - the root session id to delete.
|
|
42728
43418
|
* @returns the outcome line for the panel/notice.
|
|
@@ -42734,7 +43424,7 @@ async function run(ctx, startup, io) {
|
|
|
42734
43424
|
const plan = planSessionDeletion(records, id);
|
|
42735
43425
|
if (!plan.ok) return plan.reason;
|
|
42736
43426
|
const root = jsonlSessionRoot(persistence);
|
|
42737
|
-
if (root === void 0) return "session backend exposes no deletable artifact (deletion is unsupported on this backend)";
|
|
43427
|
+
if (root === void 0 || persistence === void 0) return "session backend exposes no deletable artifact (deletion is unsupported on this backend)";
|
|
42738
43428
|
const byId = new Map(records.map((record) => [record.header.id, record]));
|
|
42739
43429
|
const dirs = /* @__PURE__ */ new Map();
|
|
42740
43430
|
for (const node of plan.nodes) {
|
|
@@ -42744,7 +43434,15 @@ async function run(ctx, startup, io) {
|
|
|
42744
43434
|
if (dir === void 0) return `refusing to delete: unexpected artifact layout for ${node.id.slice(-12)}`;
|
|
42745
43435
|
dirs.set(node.id, dir);
|
|
42746
43436
|
}
|
|
43437
|
+
let leases;
|
|
43438
|
+
try {
|
|
43439
|
+
leases = await acquireSessionDeletionLeases(persistence, plan.nodes.map((node) => node.id));
|
|
43440
|
+
} catch (error) {
|
|
43441
|
+
if (error instanceof SessionAlreadyOwnedError) return `cannot delete ${error.sessionId.slice(-12)} — it is open in this or another process`;
|
|
43442
|
+
return `cannot safely lock sessions for deletion: ${error instanceof Error ? error.message : String(error)}`;
|
|
43443
|
+
}
|
|
42747
43444
|
let removed = 0;
|
|
43445
|
+
let outcome;
|
|
42748
43446
|
for (const node of plan.nodes) {
|
|
42749
43447
|
const dir = dirs.get(node.id);
|
|
42750
43448
|
try {
|
|
@@ -42756,10 +43454,17 @@ async function run(ctx, startup, io) {
|
|
|
42756
43454
|
}).catch(() => {});
|
|
42757
43455
|
removed += 1;
|
|
42758
43456
|
} catch (error) {
|
|
42759
|
-
|
|
43457
|
+
outcome = `delete failed for ${node.id.slice(-12)} after ${removed} of ${plan.nodes.length}: ${error instanceof Error ? error.message : String(error)}`;
|
|
43458
|
+
break;
|
|
42760
43459
|
}
|
|
42761
43460
|
}
|
|
42762
|
-
|
|
43461
|
+
outcome ??= `deleted ${removed} session${removed === 1 ? "" : "s"}`;
|
|
43462
|
+
try {
|
|
43463
|
+
await releaseSessionDeletionLeases(leases);
|
|
43464
|
+
} catch (error) {
|
|
43465
|
+
return `${outcome}; failed to release deletion locks: ${error instanceof Error ? error.message : String(error)}`;
|
|
43466
|
+
}
|
|
43467
|
+
return outcome;
|
|
42763
43468
|
};
|
|
42764
43469
|
const loadSessionTranscript = async (id, signal) => {
|
|
42765
43470
|
if (sessionQuery === void 0) throw new Error("session query is unavailable in this profile");
|
|
@@ -42859,7 +43564,10 @@ async function run(ctx, startup, io) {
|
|
|
42859
43564
|
epoch += 1;
|
|
42860
43565
|
if (previous === void 0) {
|
|
42861
43566
|
setTimeout(() => {
|
|
42862
|
-
bridge.notify(
|
|
43567
|
+
bridge.notify(t(next.resumed ? "notice.sessionResumed" : "notice.sessionCreated", {
|
|
43568
|
+
id: next.session.id.slice(-12),
|
|
43569
|
+
mode: next.mode
|
|
43570
|
+
}));
|
|
42863
43571
|
}, 0);
|
|
42864
43572
|
return;
|
|
42865
43573
|
}
|
|
@@ -42867,31 +43575,39 @@ async function run(ctx, startup, io) {
|
|
|
42867
43575
|
try {
|
|
42868
43576
|
await sessions.flush(previous.session);
|
|
42869
43577
|
} catch (error) {
|
|
42870
|
-
cleanupWarning =
|
|
43578
|
+
cleanupWarning = t("notice.flushFailed", { message: error instanceof Error ? error.message : String(error) });
|
|
42871
43579
|
}
|
|
42872
43580
|
try {
|
|
42873
43581
|
await previous.handle.dispose();
|
|
42874
43582
|
} catch (error) {
|
|
42875
|
-
|
|
42876
|
-
|
|
42877
|
-
|
|
43583
|
+
const release = t("notice.agentReleaseFailed", { message: error instanceof Error ? error.message : String(error) });
|
|
43584
|
+
cleanupWarning = cleanupWarning === void 0 ? release : `${cleanupWarning}; ${release}`;
|
|
43585
|
+
}
|
|
43586
|
+
const shortId = next.session.id.slice(-12);
|
|
43587
|
+
bridge.notify(cleanupWarning === void 0 ? t(next.resumed ? "notice.sessionResumed" : "notice.sessionCreated", {
|
|
43588
|
+
id: shortId,
|
|
43589
|
+
mode: next.mode
|
|
43590
|
+
}) : t("notice.sessionSwitchedDirty", {
|
|
43591
|
+
id: shortId,
|
|
43592
|
+
detail: cleanupWarning
|
|
43593
|
+
}), cleanupWarning === void 0 ? "info" : "warning");
|
|
42878
43594
|
});
|
|
42879
43595
|
};
|
|
42880
43596
|
const switchQueue = new SessionSwitchQueue(async (request) => {
|
|
42881
43597
|
if (!quitting) await activate(request.target);
|
|
42882
|
-
}, (error) => bridge.notify(
|
|
43598
|
+
}, (error) => bridge.notify(t("notice.sessionSwitchFailed", { message: error instanceof Error ? error.message : String(error) }), "error"));
|
|
42883
43599
|
const requestSwitch = (request) => {
|
|
42884
43600
|
if (session === void 0) {
|
|
42885
43601
|
activate(request.target).catch((error) => {
|
|
42886
|
-
bridge.notify(
|
|
43602
|
+
bridge.notify(t("notice.sessionSwitchFailed", { message: error instanceof Error ? error.message : String(error) }), "error");
|
|
42887
43603
|
});
|
|
42888
43604
|
return;
|
|
42889
43605
|
}
|
|
42890
43606
|
if (request.target.sessionId === session.id) {
|
|
42891
|
-
bridge.notify("
|
|
43607
|
+
bridge.notify(t("notice.alreadyActive"), "warning");
|
|
42892
43608
|
return;
|
|
42893
43609
|
}
|
|
42894
|
-
if (switchQueue.request(agent, request) === "queued") bridge.notify(
|
|
43610
|
+
if (switchQueue.request(agent, request) === "queued") bridge.notify(t("notice.switchQueued", { label: request.label }));
|
|
42895
43611
|
};
|
|
42896
43612
|
const resolveResumeId = async (wanted) => {
|
|
42897
43613
|
if (wanted === "") throw new Error("usage: /resume <id|prefix>");
|
|
@@ -42915,7 +43631,7 @@ async function run(ctx, startup, io) {
|
|
|
42915
43631
|
},
|
|
42916
43632
|
label: id.slice(-12)
|
|
42917
43633
|
});
|
|
42918
|
-
}, (error) => bridge.notify(
|
|
43634
|
+
}, (error) => bridge.notify(t("notice.resumeFailed", { message: error instanceof Error ? error.message : String(error) }), "error"));
|
|
42919
43635
|
};
|
|
42920
43636
|
const createSession = (mode) => {
|
|
42921
43637
|
if (session === void 0) {
|
|
@@ -42964,7 +43680,7 @@ async function run(ctx, startup, io) {
|
|
|
42964
43680
|
};
|
|
42965
43681
|
const forkSession = (argument) => {
|
|
42966
43682
|
if (session === void 0 || active === void 0) {
|
|
42967
|
-
bridge.notify("
|
|
43683
|
+
bridge.notify(t("notice.noSessionYet"), "warning");
|
|
42968
43684
|
return;
|
|
42969
43685
|
}
|
|
42970
43686
|
try {
|
|
@@ -42986,12 +43702,12 @@ async function run(ctx, startup, io) {
|
|
|
42986
43702
|
label: id.slice(-12)
|
|
42987
43703
|
});
|
|
42988
43704
|
} catch (error) {
|
|
42989
|
-
bridge.notify(
|
|
43705
|
+
bridge.notify(t("notice.forkFailed", { message: error instanceof Error ? error.message : String(error) }), "error");
|
|
42990
43706
|
}
|
|
42991
43707
|
};
|
|
42992
43708
|
const switchSession = (row) => {
|
|
42993
43709
|
if (!row.resumable) {
|
|
42994
|
-
bridge.notify("
|
|
43710
|
+
bridge.notify(t("notice.subagentsReadOnly"), "warning");
|
|
42995
43711
|
return;
|
|
42996
43712
|
}
|
|
42997
43713
|
requestSwitch({
|
|
@@ -43137,36 +43853,42 @@ async function run(ctx, startup, io) {
|
|
|
43137
43853
|
};
|
|
43138
43854
|
mountRef.current = io.mount(appElement());
|
|
43139
43855
|
if (startup.prompt !== void 0 || (startup.images?.length ?? 0) > 0) {
|
|
43140
|
-
if ((startup.images?.length ?? 0) > 0) bridge.notify(
|
|
43856
|
+
if ((startup.images?.length ?? 0) > 0) bridge.notify(t("notice.startupImages", {
|
|
43857
|
+
count: startup.images.length,
|
|
43858
|
+
plural: startup.images.length === 1 ? "" : "s"
|
|
43859
|
+
}));
|
|
43141
43860
|
inputGate.run(async (deliver) => {
|
|
43142
43861
|
const images = await saveImagePaths(startup.images ?? [], ctx.get("attachments"));
|
|
43143
|
-
if (images.length > 0) bridge.notify(
|
|
43862
|
+
if (images.length > 0) bridge.notify(t("notice.startupImagesAttached", {
|
|
43863
|
+
count: images.length,
|
|
43864
|
+
plural: images.length === 1 ? "" : "s"
|
|
43865
|
+
}));
|
|
43144
43866
|
deliver({
|
|
43145
43867
|
text: startup.prompt ?? "",
|
|
43146
43868
|
mode: "followup",
|
|
43147
43869
|
images
|
|
43148
43870
|
});
|
|
43149
43871
|
}).catch((error) => {
|
|
43150
|
-
bridge.notify(
|
|
43872
|
+
bridge.notify(t("notice.initialPromptFailed", { message: error instanceof Error ? error.message : String(error) }), "error");
|
|
43151
43873
|
});
|
|
43152
43874
|
}
|
|
43153
43875
|
async function copyLastResponse() {
|
|
43154
43876
|
const text = latestAssistantText(store.getView());
|
|
43155
|
-
if (text === void 0) return "
|
|
43877
|
+
if (text === void 0) return t("notice.copyEmpty");
|
|
43156
43878
|
await copyText(text);
|
|
43157
|
-
return "copied
|
|
43879
|
+
return t("notice.copied");
|
|
43158
43880
|
}
|
|
43159
43881
|
if (statuslineWarning !== void 0) setTimeout(() => {
|
|
43160
|
-
bridge.notify("
|
|
43882
|
+
bridge.notify(t("notice.statuslineConfigUnreadable", { message: statuslineWarning }), "warning");
|
|
43161
43883
|
}, 50);
|
|
43162
43884
|
if (languageWarning !== void 0) setTimeout(() => {
|
|
43163
43885
|
bridge.notify(t("notice.languageConfigUnreadable", { message: languageWarning }), "warning");
|
|
43164
43886
|
}, 0);
|
|
43165
43887
|
if (themeWarning !== void 0) setTimeout(() => {
|
|
43166
|
-
bridge.notify("
|
|
43888
|
+
bridge.notify(t("notice.themeConfigUnreadable", { message: themeWarning }), "warning");
|
|
43167
43889
|
}, 50);
|
|
43168
43890
|
if (animationsWarning !== void 0) setTimeout(() => {
|
|
43169
|
-
bridge.notify("
|
|
43891
|
+
bridge.notify(t("notice.animationsConfigUnreadable", { message: animationsWarning }), "warning");
|
|
43170
43892
|
}, 50);
|
|
43171
43893
|
resolveEditorKeysStartupHint(editorKeysEnv).then((hint) => {
|
|
43172
43894
|
if (hint === void 0) return;
|