tabby-agentdeck 1.1.3 → 1.2.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/README.ko.md +26 -574
  3. package/README.md +25 -545
  4. package/dist/agents.d.ts +15 -0
  5. package/dist/codexHooks.d.ts +23 -0
  6. package/dist/codexMeta.d.ts +87 -0
  7. package/dist/config.d.ts +120 -2
  8. package/dist/cursorVisibility.d.ts +33 -1
  9. package/dist/deck.service.d.ts +182 -19
  10. package/dist/devReload.service.d.ts +48 -0
  11. package/dist/devReload.stub.d.ts +19 -0
  12. package/dist/dock.d.ts +15 -0
  13. package/dist/i18n/af.d.ts +83 -0
  14. package/dist/i18n/bg.d.ts +83 -0
  15. package/dist/i18n/cs.d.ts +83 -0
  16. package/dist/i18n/da.d.ts +83 -0
  17. package/dist/i18n/de.d.ts +83 -0
  18. package/dist/i18n/en.d.ts +83 -0
  19. package/dist/i18n/es.d.ts +83 -0
  20. package/dist/i18n/fr.d.ts +83 -0
  21. package/dist/i18n/hr.d.ts +83 -0
  22. package/dist/i18n/id.d.ts +83 -0
  23. package/dist/i18n/it.d.ts +83 -0
  24. package/dist/i18n/ja.d.ts +83 -0
  25. package/dist/i18n/ko.d.ts +83 -0
  26. package/dist/i18n/pl.d.ts +83 -0
  27. package/dist/i18n/pt.d.ts +83 -0
  28. package/dist/i18n/ru.d.ts +83 -0
  29. package/dist/i18n/sr.d.ts +83 -0
  30. package/dist/i18n/sv.d.ts +83 -0
  31. package/dist/i18n/tr.d.ts +83 -0
  32. package/dist/i18n/uk.d.ts +83 -0
  33. package/dist/i18n/zh-CN.d.ts +83 -0
  34. package/dist/i18n/zh-TW.d.ts +83 -0
  35. package/dist/i18n.d.ts +3618 -0
  36. package/dist/index.d.ts +3 -1
  37. package/dist/index.js +1 -2
  38. package/dist/keybind.d.ts +121 -0
  39. package/dist/meta.d.ts +83 -0
  40. package/dist/nav.d.ts +29 -0
  41. package/dist/notify.service.d.ts +149 -1
  42. package/dist/project-root.d.ts +1 -1
  43. package/dist/reload.service.d.ts +57 -0
  44. package/dist/sessionLedger.d.ts +11 -6
  45. package/dist/settings.component.d.ts +133 -8
  46. package/dist/status.service.d.ts +30 -2
  47. package/dist/statusLine.d.ts +27 -0
  48. package/dist/update.d.ts +19 -5
  49. package/dist/update.service.d.ts +29 -20
  50. package/dist/viewPanel.d.ts +13 -3
  51. package/hooks/agentdeck-codex-notify.ps1 +5 -2
  52. package/hooks/agentdeck-label.js +1 -1
  53. package/hooks/agentdeck-notify.ps1 +32 -12
  54. package/hooks/agentdeck-statusline.mjs +351 -0
  55. package/package.json +4 -2
  56. package/updater/agentdeck-update.ps1 +50 -27
  57. package/dist/index.js.map +0 -1
@@ -1,5 +1,28 @@
1
1
  export declare const CODEX_EVENTS: string[];
2
2
  export declare function codexHooksPath(): string;
3
+ /** Codex 가 훅 신뢰 상태를 적어 두는 곳 — `hooks.json` 과 **다른 파일**이다 */
4
+ export declare function codexConfigPath(): string;
5
+ /**
6
+ * Codex 가 **꺼 둔** 우리 훅 목록 (`config.toml` 의 `[hooks.state]`).
7
+ *
8
+ * **`hooks.json` 에 우리 항목이 있다고 훅이 도는 것이 아니다.** Codex 는 훅을 처음 보면
9
+ * `/hooks` 에서 사람이 신뢰할 때까지 멈춰 두고, 그 결과를 `config.toml` 에 따로 적는다:
10
+ *
11
+ * [hooks.state.'C:\\Users\\…\\hooks.json:user_prompt_submit:0:0']
12
+ * trusted_hash = "sha256:…"
13
+ * enabled = false
14
+ *
15
+ * 2026-09-14 실측: 이 PC 는 `session_start`·`user_prompt_submit`·`stop` 셋이 꺼져 있었고,
16
+ * 그 셋이 정확히 **진행중 진입과 완료 신호**라 사이드바가 Codex 상태를 영영 못 받았다
17
+ * (상태 보고 폴더에 codex 파일 0건). 설치만 확인하고 "연동됨" 이라 말하면 사용자는
18
+ * 아무 일도 안 일어나는 이유를 볼 수 없다 — 그래서 이 값을 따로 읽어 화면에 말한다.
19
+ *
20
+ * TOML 파서를 들이지 않는 이유 — 읽는 것이 **한 종류의 줄**뿐이고, 그 모양이 깨지면
21
+ * 빈 목록으로 떨어져 "모른다" 가 된다(없는 경고를 지어내지 않는다).
22
+ */
23
+ export declare function codexDisabledEvents(): string[];
24
+ /** `user_prompt_submit` → `UserPromptSubmit` (config.toml 은 snake_case, hooks.json 은 PascalCase) */
25
+ export declare function codexEventLabel(snake: string): string;
3
26
  export declare function stripCodexHooks(settings: any): any;
4
27
  export declare function mergeCodexHooks(settings: any, script: string): any;
5
28
  export declare function codexHooksInstalled(): boolean;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Codex 세션 기록(rollout jsonl)에서 "지금 이 탭" 줄에 쓸 값을 뽑는다.
3
+ *
4
+ * **왜 다른 경로인가** — Claude Code 는 `statusLine` 이라는 통보 창구가 있어 그쪽 입력을 베끼면
5
+ * 되지만(`hooks/agentdeck-statusline.mjs`), Codex 에는 그런 것이 없다. 대신 Codex 는 자기 세션
6
+ * 기록에 같은 값을 남긴다(2026-09-14 실측, `~/.codex/sessions/…/rollout-*.jsonl`):
7
+ *
8
+ * `turn_context` → `model: "gpt-6-astra"` · `effort: "low"` (턴마다 새로 나온다)
9
+ * `session_meta` → `cwd` · `cli_version` (+ 옛 버전은 `context_window` 가 **숫자**)
10
+ * `event_msg`/`token_count` → `rate_limits.primary/secondary` (`used_percent`·`window_minutes`·`resets_at`)
11
+ * `info.last_token_usage.total_tokens` · `info.model_context_window`
12
+ *
13
+ * **창 크기가 사는 곳이 버전마다 다르다.** 0.154.0 부터 `session_meta.context_window` 는 숫자가
14
+ * 아니라 `{window_id: "…"}` 객체이고, 진짜 토큰 수는 `token_count.info.model_context_window` 로
15
+ * 옮겨 갔다 (2026-09-14 실측, gpt-6-astra / cli 0.154.0: `{"window_id":"01a09f5e-…"}` 와 `258400`).
16
+ * 그래서 둘 다 읽는다 — 하나만 읽으면 그 버전에서 컨텍스트% 칸이 통째로 사라진다.
17
+ *
18
+ * **이 파일은 순수하다** — fs·DOM·tabby 를 건드리지 않는다(`agents.ts`·`meta.ts` 와 같은 규칙).
19
+ * 줄을 먹이면 상태가 갱신되고, 서비스 쪽은 파일을 증분으로 읽어 그 줄만 넘긴다.
20
+ */
21
+ /** 한도 한 칸 — Codex 는 창 길이를 분으로 준다 */
22
+ export interface CodexWindow {
23
+ pct: number;
24
+ /** 창 길이(분). 300 = 5시간, 10080 = 7일 */
25
+ windowMinutes: number;
26
+ /** 리셋 시각 (epoch **초**) */
27
+ resetsAt: number;
28
+ }
29
+ export interface CodexState {
30
+ model: string;
31
+ effort: string;
32
+ cwd: string;
33
+ cliVersion: string;
34
+ /**
35
+ * 세션의 컨텍스트 창 크기(토큰). Codex 가 안 적어 주는 버전도 있다.
36
+ * 0.154.0 부터는 `session_meta` 가 아니라 `token_count.info.model_context_window` 가 원천이다
37
+ */
38
+ contextWindow: number;
39
+ /** 마지막 요청이 쓴 토큰 — 컨텍스트 사용률의 분자 */
40
+ lastTokens: number;
41
+ primary: CodexWindow | null;
42
+ secondary: CodexWindow | null;
43
+ /**
44
+ * 개행으로 끝나지 않은 마지막 조각 — 다음 청크 앞에 이어 붙인다.
45
+ *
46
+ * **증분 읽기에는 이게 반드시 있어야 한다.** 서비스는 2초마다 `size - lastSize` 만큼 읽는데
47
+ * Codex 는 그동안에도 쓰고 있어 읽은 청크의 마지막 줄은 대개 미완성이다. 이어 붙이지 않으면
48
+ * 그 줄을 버리는데 `lastSize` 는 이미 전진했으므로 **나머지 절반도 다음 판에서 버려진다** —
49
+ * 턴 끝의 마지막 `token_count`(한도·컨텍스트%)가 통째로 유실돼 값이 옛 상태로 굳는다
50
+ * (2026-09-15 코드리뷰 지적). `subagents.ts` 의 `feedChunk` 가 같은 이유로 같은 것을 한다.
51
+ */
52
+ pending: string;
53
+ /** 먹인 줄 수 / 버린 줄 수 (진단용) */
54
+ lines: number;
55
+ skipped: number;
56
+ /** `MAX_PENDING` 을 넘겨 버린 꼬리 수 (진단용 — 0 이 아니면 뭔가 이상하다) */
57
+ dropped: number;
58
+ }
59
+ export declare function createCodexState(): CodexState;
60
+ /**
61
+ * 줄 하나를 먹인다. **마지막 값이 이긴다** — `turn_context` 는 턴마다, `token_count` 는 요청마다
62
+ * 새로 나오므로 나중 줄이 지금 상태다. 못 읽는 줄은 세기만 하고 버린다(증분 읽기의 반쪽 줄).
63
+ */
64
+ export declare function feedCodexLine(state: CodexState, line: string): void;
65
+ export declare function feedCodexChunk(state: CodexState, chunk: string): void;
66
+ /** 컨텍스트 사용률(%) — 창 크기를 모르면 null (지어내지 않는다) */
67
+ export declare function codexContextPct(state: CodexState): number | null;
68
+ /**
69
+ * 한도 두 칸을 Claude 쪽과 **같은 자리**(5시간/7일)에 맞춘다.
70
+ *
71
+ * Codex 는 이름 대신 창 길이(분)를 주므로 그 값으로 가른다 — 실측은 `primary: 300분`(5시간),
72
+ * `secondary: 10080분`(7일)이다. 길이가 그 둘이 아니면 **넣지 않는다**: 다른 길이의 값을 `5h` 자리에
73
+ * 그리면 화면이 거짓말을 한다.
74
+ */
75
+ export declare function codexLimits(state: CodexState): {
76
+ fiveHourPct: number | null;
77
+ fiveHourResetsAt: number;
78
+ sevenDayPct: number | null;
79
+ sevenDayResetsAt: number;
80
+ };
81
+ /**
82
+ * Codex 계정 메일 — `~/.codex/auth.json` 의 `tokens.id_token`(JWT) 가운데 조각에 들어 있다.
83
+ *
84
+ * **서명을 확인하지 않는다.** 이건 인증이 아니라 "지금 이 컴퓨터가 누구로 로그인돼 있나" 를
85
+ * 화면에 적기 위한 읽기다. 토큰 자체는 어디에도 싣지 않는다.
86
+ */
87
+ export declare function emailFromIdToken(jwt: string | null | undefined): string;
package/dist/config.d.ts CHANGED
@@ -134,6 +134,14 @@ export declare class AgentDeckConfigProvider extends ConfigProvider {
134
134
  * 설정 창에서 토글한다 (recovery.service.ts).
135
135
  */
136
136
  noTabRecovery: boolean;
137
+ /**
138
+ * 개발자 옵션 — `dist/index.js` 가 바뀌면 세션을 살린 채 창을 리로드한다(devReload.service.ts).
139
+ *
140
+ * **개발 빌드 + 개발 설치(소스 트리)에서만 의미가 있다.** npm 배포 빌드(`npm pack` →
141
+ * `build:release`)는 기능 코드를 스텁으로 바꿔 끼우므로 이 값이 참이어도 아무것도 안 하고
142
+ * 설정 화면에도 안 보인다.
143
+ */
144
+ devMode: boolean;
137
145
  /**
138
146
  * 기동할 때 npm 레지스트리를 보고 **새 버전을 받아 둔다** (적용은 다음 기동).
139
147
  *
@@ -153,6 +161,26 @@ export declare class AgentDeckConfigProvider extends ConfigProvider {
153
161
  updateRegistry: string;
154
162
  /** PTY 출력이 이 시간(ms) 이상 없으면 running -> idle */
155
163
  idleAfterMs: number;
164
+ /**
165
+ * **훅이 고정한 `진행중` 탭**이 이 시간(ms) 동안 아무 기척도 없으면 고정을 풀고 idle 로
166
+ * 내린다. 0 이면 끈다 (0.17.0 이전 동작 — 영영 안 내려온다).
167
+ *
168
+ * 왜 따로 두나 — 위 `idleAfterMs`(2.5초)는 **자동 감지** 탭의 값이라 고정된 탭에 쓸 수
169
+ * 없다. 훅은 도구 하나가 끝날 때(`PostToolUse`)에야 말하므로 2분짜리 빌드 중에는 몇 분씩
170
+ * 조용하고, 그 값을 그대로 적용하면 일하는 탭이 매번 idle 로 깜빡인다.
171
+ *
172
+ * 왜 필요한가 — 훅에 "세션이 끝났다" 이벤트가 없다(`claudeHooks.ts` PLAN). Ctrl+C 로 끊거나
173
+ * 창을 닫거나 CLI 가 죽으면 마지막 `running` 이 그대로 남고, 고정된 탭에는 배지가 내려올
174
+ * 길이 하나도 없었다 (`status.service.ts tickIdle` 주석). 사이드바가 "작업중" 이라고
175
+ * 말하는데 실은 아무것도 안 하고 있는 탭이 그것이다.
176
+ *
177
+ * 5분인 이유 — 기척으로 **훅 보고·PTY 출력·작업중 신호 셋 다** 인정하므로(셋 중 최신에서
178
+ * 잰다) 살아 있는 세션은 이 값에 걸릴 일이 없다. 사람이 "어? 안 바뀌네" 하고 알아채는
179
+ * 시간보다는 짧아야 해서 시간 단위로 두지 않았다.
180
+ *
181
+ * `승인대기`·`한도`는 이 시계를 타지 않는다 — 조용한 것이 정상인 상태다
182
+ */
183
+ staleAfterMs: number;
156
184
  /** 출력 패턴으로 상태를 자동 추론할지 */
157
185
  autoDetect: boolean;
158
186
  /** 사이드바에 경과 시간 표시 */
@@ -172,6 +200,15 @@ export declare class AgentDeckConfigProvider extends ConfigProvider {
172
200
  * (`notify.service` `scanSubagents`).
173
201
  */
174
202
  subagentCount: boolean;
203
+ /**
204
+ * 사이드바 하단에 **지금 이 탭** 줄을 고정한다 — 도는 모델·effort·계정·컨텍스트%·
205
+ * 5h/7d 한도%. 탭을 바꾸면 그 탭 값으로 다시 그린다.
206
+ *
207
+ * 켜 두는 것만으로는 아무 일도 일어나지 않는다 — 이 값들은 **statusLine stdin 에만**
208
+ * 내려오므로 설정 창에서 `statusLine 연결` 을 설치해야 보고가 온다(statusLine.ts).
209
+ * 설치 전에는 줄이 통째로 접혀 있어 화면이 지금과 같다.
210
+ */
211
+ metaLine: boolean;
175
212
  /**
176
213
  * 사이드바 탭을 상태 우선순위(waiting → error → running → done → idle)로 정렬한다.
177
214
  * 같은 상태끼리는 탭 순서를 지킨다. 기본은 끔 — 순정 탭바와 같은 탭 순서로 보인다.
@@ -272,11 +309,20 @@ export declare class AgentDeckConfigProvider extends ConfigProvider {
272
309
  * UUID 목록에서 자기 작업을 찾는 것이라 **라벨을 붙여 보여주는 것** 이 이 기능의 전부다.
273
310
  */
274
311
  resumeList: boolean;
275
- /** 그룹당 몇 줄까지 보여줄지. 0 이면 제한 없음 (목록이 길어지면 살아 있는 탭이 밀린다) */
312
+ /**
313
+ * 몇 줄까지 보여줄지. **0 = 제한 없음이고 그게 기본이다.**
314
+ *
315
+ * 예전에는 5 였다 — 서랍이 그룹마다 목록 **안**에 있어서, 길어지면 살아 있는 탭이
316
+ * 아래로 밀려났기 때문이다. 지금 서랍은 사이드바 바닥에 붙은 **스크롤 상자**라
317
+ * 몇 줄이 들어 있든 살아 있는 탭을 밀지 않는다. 잘라 둘 이유가 사라졌고,
318
+ * 잘려 나간 세션은 사람이 찾을 방법이 아예 없었다
319
+ * (2026-09-14 유저: "기껏 저장한걸 굳이 안쓸 이유는 없을 듯").
320
+ */
276
321
  resumeListLimit: number;
277
322
  /**
278
323
  * 며칠치까지. 기간 밖 세션은 **화면에서만** 빠지고 원장·기록은 그대로다.
279
- * stat 단계에서 잘라내므로 이 값이 작을수록 펼치는 순간이 싸다.
324
+ * 위와 같은 이유로 0(제한 없음)이 기본이다 — 한 달 전 작업을 이어받는 일이
325
+ * 드물긴 해도, 드물다는 것이 "못 찾게 한다" 의 근거는 아니다.
280
326
  */
281
327
  resumeListDays: number;
282
328
  /** 지난 세션 줄을 펼쳐 둔 그룹 키 (`collapsedGroups` 와 반대로 **펼침**을 적는다 — 기본이 접힘이라서) */
@@ -309,6 +355,25 @@ export declare class AgentDeckConfigProvider extends ConfigProvider {
309
355
  * false 로 두면 터미널 앱에 리터럴 Ctrl+V(0x16, quoted-insert)를 보낼 수 있다
310
356
  */
311
357
  claimCtrlV: boolean;
358
+ /**
359
+ * `Ctrl+Shift+T`(⌘+T) 를 agentdeck 새 탭이 받는다 — 순정 `new-tab` 에서 그 키를 뗀다.
360
+ * false 면 떼지 않고 우리 핫키도 무시한다(순정 그대로). 순정 새 탭은 기본 프로필로 열어
361
+ * 훅이 탭을 못 찾으므로, 이 플러그인을 쓰는 동안은 켜 두는 것이 맞다
362
+ */
363
+ claimNewTabKey: boolean;
364
+ /**
365
+ * `Ctrl+Shift+R` 을 agentdeck 화면 복구가 받는다 — 순정 `rename-tab` 에서 그 키를 뗀다.
366
+ * false 면 떼지 않고 우리 핫키도 무시한다(순정 그대로). 탭 이름은 사이드바 더블클릭으로 바꾼다
367
+ */
368
+ claimRepairKey: boolean;
369
+ /**
370
+ * 순정 `close-pane`(포커스된 분할 패널 닫기)이 **비어 있으면** 이 키를 채운다.
371
+ * Tabby 기본표는 이 항목이 빈 채로 오고, agentdeck 은 짧은 우클릭을 복사/붙여넣기로
372
+ * 쓰기 때문에 컨텍스트 메뉴로 패널을 닫기가 번거롭다. `Ctrl-Shift-Q` 는 순정 네 패키지
373
+ * 기본표·agentdeck 어디에도 없는 키(2026-09-16 grep 0건).
374
+ * 이미 사람이 채워 둔 표는 건드리지 않는다. 빈 문자열이면 아무것도 하지 않는다.
375
+ */
376
+ closePaneKey: string;
312
377
  /**
313
378
  * Shift+Enter / Ctrl+Enter 를 캡처 단계에서 직접 줄바꿈으로 처리한다.
314
379
  *
@@ -629,6 +694,59 @@ export declare class AgentDeckConfigProvider extends ConfigProvider {
629
694
  * Backspace·`-` 이다. 다른 플러그인과 부딪히면 설정 창에서 바꾼다.
630
695
  */
631
696
  'agentdeck-focus-list': string[];
697
+ /**
698
+ * **사이드바에 보이는 순서로** N 번째 세션으로 건너뛴다 (`Ctrl-1` … `Ctrl-9`).
699
+ *
700
+ * Tabby 순정 `tab-1…tab-10` 과 다른 기능이다 — 그쪽은 `app.tabs`(탭 바의 순서)를 세고
701
+ * 이쪽은 **사이드바 화면에 보이는 줄**을 센다. 정렬(`sortByStatus`)·그룹·검색·상태 필터가
702
+ * 걸리면 둘은 어긋나고, 사람이 보고 있는 것은 사이드바다.
703
+ *
704
+ * 기본값이 `Ctrl-N` 인 근거 — Tabby 의 Windows/Linux 기본은 `tab-N` = `Alt-N` 이라
705
+ * 겹치지 않는다(`tabby-core` 의 windows/linux 기본 핫키표). macOS 기본은 `⌘-N` 이라
706
+ * 역시 겹치지 않는다. 터미널 쪽도 `Ctrl+숫자`는 제어문자를 내지 않으므로 뺏어도 잃는 키가
707
+ * 없다.
708
+ *
709
+ * 9 까지만 두는 이유 — 열 번째부터는 눈으로 세는 것이 키를 누르는 것보다 느리다.
710
+ * 그 위는 `Ctrl-Shift-L` 로 목록에 들어가 ↑↓ 로 간다
711
+ */
712
+ 'agentdeck-jump-1': string[];
713
+ 'agentdeck-jump-2': string[];
714
+ 'agentdeck-jump-3': string[];
715
+ 'agentdeck-jump-4': string[];
716
+ 'agentdeck-jump-5': string[];
717
+ 'agentdeck-jump-6': string[];
718
+ 'agentdeck-jump-7': string[];
719
+ 'agentdeck-jump-8': string[];
720
+ 'agentdeck-jump-9': string[];
721
+ /**
722
+ * 사이드바 하단 버튼 세 개의 키.
723
+ *
724
+ * **새 탭은 순정 `new-tab`(Ctrl+Shift+T · macOS ⌘+T)을 대체한다.** 순정은 `terminal.openTab()`
725
+ * 으로 기본 프로필을 열어서 `AGENTDECK_TAB` 이 안 심기고 훅이 탭을 못 찾는다. 같은 키에
726
+ * 우리 것을 얹고 순정 표에서 그 키를 뗀다(`ensureNewTabHotkey`, `claimNewTabKey`) —
727
+ * 안 떼면 한 번 눌러 탭이 둘 열린다. `Ctrl+V` 를 가져온 방식(`ensurePasteHotkey`)과 같다.
728
+ *
729
+ * `Ctrl-Shift-O` 는 순정 네 패키지(core·terminal·local·settings) 기본표에 없다
730
+ * (2026-09-16 grep 0건 — 순정이 쓰는 것은 `Ctrl-Shift-{A,C,D,E,F,I,P,R,S,T,V,W,Z}`).
731
+ *
732
+ * **화면 복구는 순정 `rename-tab`(Ctrl+Shift+R)을 대체한다** (2026-09-17). 이름 바꾸기는
733
+ * 사이드바 더블클릭이 주 경로라 키를 잃어도 아쉽지 않고, R(repair)이 외우기 쉽다.
734
+ * 새 탭과 같은 방식 — 우리 것을 얹고 순정 표에서 뗀다(`ensureRepairHotkey`, `claimRepairKey`).
735
+ * 옛 기본값 `Ctrl-Shift-U` 가 config 에 그대로 저장돼 있으면(사람이 안 바꾼 것) R 로 옮긴다.
736
+ */
737
+ 'agentdeck-new-tab': string[];
738
+ 'agentdeck-view': string[];
739
+ /** 패널의 파일 ↔ 변경 전환 — 기본은 미배정 (설정 창 단축키 절에서 매긴다) */
740
+ 'agentdeck-view-mode': any[];
741
+ 'agentdeck-repair': string[];
632
742
  };
633
743
  };
634
744
  }
745
+ /**
746
+ * 우리 기본 핫키 표 — 설정 창 **단축키** 절의 `기본값` 버튼이 쓴다.
747
+ *
748
+ * `defaults` 는 인스턴스 필드라 클래스에서 바로 못 읽는다. ConfigProvider 는 생성자 의존이
749
+ * 없으므로 하나 만들어 꺼내는 것이 가장 싸다. 순정 `close-pane` 은 우리 표에 없고
750
+ * `agentDeck.closePaneKey` 가 원천이라 여기서 함께 얹는다(`ensureClosePaneHotkey` 와 같은 값).
751
+ */
752
+ export declare function defaultHotkeys(): Record<string, string[]>;
@@ -3,7 +3,39 @@ export declare class CursorVisibilityStream {
3
3
  corrections: number;
4
4
  write(data: string, enabled: boolean): string;
5
5
  }
6
+ /**
7
+ * `CSI Ps SP q` (DECSCUSR) 의 **깜빡이는 스타일을 고정 스타일로** 바꾼다.
8
+ *
9
+ * Codex 0.154.0 은 프레임마다 `ESC [ 0 q` 를 보낸다(`test/cursor.cjs` 의 실측 `lastPacket`).
10
+ * 0 은 "깜빡이는 블록" 이라, 터미널은 시킨 대로 커서를 깜빡인다 — **순정 Tabby 에서도 똑같이
11
+ * 난다**(2026-09-15 유저 확인). 표시/숨김이 아니라 스타일이므로 가시성 필터로는 막을 수 없다.
12
+ *
13
+ * 모양은 그대로 두고 깜빡임만 끈다: 0·1(블록)→2, 3(밑줄)→4, 5(막대)→6.
14
+ */
15
+ export declare class SteadyCursorStyleStream {
16
+ private pending;
17
+ changes: number;
18
+ write(data: string, enabled: boolean): string;
19
+ }
6
20
  /** Install at the frontend's write boundary; do not inject into xterm's
7
21
  * asynchronously parsed stream or touch the application's saved cursor slot.
8
22
  */
9
- export declare function installCursorVisibilityFix(frontend: any, enabled: (data: string) => boolean, onCorrection: () => void): void;
23
+ export declare function installCursorVisibilityFix(frontend: any, enabled: (data: string) => boolean, onCorrection: () => void, onTrace?: (summary: string) => void): void;
24
+ /** ConPTY wraps **every** redraw chunk with DECTCEM hide/show while Codex is
25
+ * still painting. Those toggles are noise, not intent — forwarding them makes the
26
+ * cursor flicker at the redraw rate (2026-09-15 measured: the cursor cell flipped
27
+ * every 100-200ms, 5-10 Hz, during Codex's startup animation).
28
+ *
29
+ * So strip them and keep only the application's **latest intent**. The installer
30
+ * applies a visibility change to the terminal *only when that intent differs from
31
+ * what is already on screen*, so a steady cursor stays steady. Text and position
32
+ * commands pass through untouched.
33
+ */
34
+ export declare class StableCursorStream {
35
+ private pending;
36
+ /** 애플리케이션이 마지막으로 말한 의도 — **화면 상태가 아니다** (그건 설치부가 쥔다) */
37
+ visible: boolean;
38
+ /** 걷어낸 표시 명령 수 (진단용) */
39
+ deferredShows: number;
40
+ write(data: string, enabled: boolean): string;
41
+ }
@@ -62,6 +62,10 @@ export declare class AgentDeckService {
62
62
  private zone;
63
63
  private sidebar;
64
64
  private listEl;
65
+ /** 사이드바 하단 "지금 이 탭" 줄 (모델·계정·한도). 목록과 버튼 줄 사이에 고정된다 */
66
+ private nowEl;
67
+ /** 지난 세션 서랍 — 목록 **밖**, "지금 이 탭" 줄 바로 위. 펼치면 위로 자란다 */
68
+ private resumeEl;
65
69
  private windowEl;
66
70
  private mainEl;
67
71
  private observer;
@@ -134,6 +138,35 @@ export declare class AgentDeckService {
134
138
  private lastSidebarW;
135
139
  /** 직전에 적용한 도킹 방향 — 크기가 같아도 방향이 바뀌면 다시 그려야 한다 */
136
140
  private lastDock;
141
+ /**
142
+ * 판정 결과를 pane 마다 **짧게** 기억한다 — `profileForPane` 은 화면에 글자를 쓸 때마다
143
+ * 불리는 자리라(커서 보정의 `enabled`, 키보드, 주기 검사) 매번 처음부터 다시 정하면
144
+ * ① 답이 판마다 흔들려 화면이 깜빡이고 ② 버퍼 60줄 훑기가 write 마다 돈다.
145
+ *
146
+ * 무효화 배선을 따로 두지 않고 **짧은 유통기한**으로 끝낸다 — 훅이 다른 에이전트를
147
+ * 말하면 그 다음 판(최대 PROFILE_MEMO_MS 뒤)에 반영되고, 사람 눈에는 즉시나 다름없다.
148
+ * 세션(pty)이 갈리면 그 자체로 무효다 — 같이 적어 두고 비교한다.
149
+ */
150
+ private profileMemo;
151
+ /**
152
+ * 회귀가 못 박아 둔 pane/tab 의 에이전트 (`__agentdeck.pinAgent`).
153
+ *
154
+ * **제품 동작에는 아무도 여기에 쓰지 않는다** — 비어 있는 Map 이고 `profileForPane` 이
155
+ * `size` 만 본다. 회귀가 끝나면 프로브가 스스로 비운다(`pinAgent(target, null)`).
156
+ * 배포본에 남는 것을 허용하는 이유는 `judge`·`agentOf` 와 같다 — 검증 도구가 제품 규칙의
157
+ * 사본을 들지 않게 하려면 제품이 창구를 열어 주는 편이 싸다.
158
+ */
159
+ private pinnedAgents;
160
+ /**
161
+ * 지금 못 박혀 있는 개수 — `WeakMap` 에는 `size` 가 없다.
162
+ *
163
+ * 굳이 `WeakMap` 인 이유: 이 진단구는 배포 번들에도 실리는데, 프로브가 예외로 죽어
164
+ * `pinAgent(target, null)` 을 못 부르면 강한 Map 은 그 pane 과 xterm 버퍼를 영원히 붙든다.
165
+ * 세는 값만 따로 두면 평소 경로(`profileForPane`)는 정수 비교 한 번으로 끝난다.
166
+ */
167
+ private pinnedCount;
168
+ /** 직전 창 박스의 왼쪽 위 (`left:top`) — 크기가 같아도 자리가 옮겨지면 다시 앉혀야 한다 */
169
+ private lastBoxKey;
137
170
  /** 경계선 드래그(크기)와 헤더 드래그(도킹 방향)를 맡는다 */
138
171
  private dock;
139
172
  private refitQueued;
@@ -335,6 +368,46 @@ export declare class AgentDeckService {
335
368
  * 감시 루프도 돌지 않는다. 그 셋이 예전에 화면을 깨뜨린 장본인이다.
336
369
  */
337
370
  private announceSizeOnOpen;
371
+ /**
372
+ * `Ctrl+Shift+T`(⌘+T) 를 순정 `new-tab` 에서 **걷어내** 우리 `agentdeck-new-tab` 만 남긴다.
373
+ *
374
+ * 순정 처리자(`tabby-local` LocalTerminalModule → `terminal.openTab()`)는 같은 `hotkey$` 를
375
+ * 구독하고 있어서, 두 id 에 같은 키가 매여 있으면 HotkeysService 가 하나만 고르긴 하지만
376
+ * (길이 같은 후보 중 config 삽입 순서 앞의 것 — `ensurePasteHotkey` 주석) 그게 어느 쪽일지는
377
+ * 저장 순서에 달려 있다. 표에서 떼는 것이 유일하게 확실한 방법이다.
378
+ *
379
+ * `claimNewTabKey: false` 면 건드리지 않는다 — 그때는 우리 핸들러도 무시하므로(hotkey$ 구독)
380
+ * 순정 그대로다. 게이트를 켰다 끄면 뗀 키는 되살리지 않는다: 사람이 손으로 바꾼 표를 우리가
381
+ * 다시 덮는 것이 더 나쁘고, 설정 창에서 한 줄이면 복구된다.
382
+ */
383
+ private ensureNewTabHotkey;
384
+ /**
385
+ * 순정 `close-pane`(포커스된 분할 패널 닫기)이 비어 있으면 `closePaneKey` 로 채운다.
386
+ *
387
+ * 왜 필요한가 — Tabby 기본표는 `close-pane: []` 로 온다(config.yaml hotkeys). 패널을 닫는
388
+ * 다른 길은 우클릭 컨텍스트 메뉴인데 agentdeck 이 짧은 우클릭을 복사/붙여넣기로 가져가
389
+ * (`claimRightClick`) 길게 눌러야만 메뉴가 뜬다. 셸에 `exit` 를 치는 방법만 남는다.
390
+ *
391
+ * 채우기만 하고 빼앗지는 않는다 — `ensureNewTabHotkey` 와 달리 순정 처리자와 경쟁하는
392
+ * 것이 아니라 순정 처리자를 **쓰는** 것이므로 표에 키를 넣어 주면 끝이다. 사람이 이미
393
+ * 무언가 매어 두었으면(비어 있지 않으면) 그대로 둔다. `closePaneKey` 가 빈 문자열이면 건너뛴다.
394
+ */
395
+ private ensureClosePaneHotkey;
396
+ /**
397
+ * `Ctrl+Shift+R` 을 순정 `rename-tab` 에서 **걷어내** 우리 `agentdeck-repair` 만 남긴다.
398
+ *
399
+ * 방식과 이유는 `ensureNewTabHotkey` 와 같다 — 같은 키가 두 id 에 매여 있으면 어느 쪽이
400
+ * 발화할지 저장 순서에 달리므로 표에서 떼는 것만이 확실하다. 탭 이름 바꾸기는 사이드바
401
+ * 더블클릭이 주 경로라 키를 잃어도 아쉽지 않다.
402
+ *
403
+ * 옛 기본값 이관 — 2026-09-17 전에는 기본이 `Ctrl-Shift-U` 였고 Tabby 는 기본값도 config.yaml 에
404
+ * 그대로 써 두므로, 사람이 손대지 않은 표에도 `['Ctrl-Shift-U']` 가 남아 있다. **정확히 옛 기본값
405
+ * 하나뿐일 때만** 새 기본값으로 바꾼다 — 다른 값이면 사람이 고른 것이니 건드리지 않는다.
406
+ *
407
+ * `claimRepairKey: false` 면 건드리지 않고 우리 핸들러도 무시한다(hotkey$ 구독). 게이트를 켰다
408
+ * 끄면 뗀 키는 되살리지 않는다 — `ensureNewTabHotkey` 와 같은 판단.
409
+ */
410
+ private ensureRepairHotkey;
338
411
  /**
339
412
  * Ctrl+V 를 **핫키 테이블에서 걷어낸다** (실제 처리는 claimCtrlVKey 가 한다).
340
413
  *
@@ -604,15 +677,36 @@ export declare class AgentDeckService {
604
677
  /**
605
678
  * 이 탭에서 도는 에이전트 CLI 가 뭔지 — 상태 감지 패턴과 이미지 붙여넣기 키가 앱마다 다르다.
606
679
  *
607
- * 프로세스 트리를 먼저 본다 (`session.getChildProcesses()`). npm 전역 래퍼를 거치면
608
- * 커맨드가 node/pwsh 로만 보일 수 있어서, 그때는 탭 제목으로 한 번 더 본다.
609
- * 어느 조각을 어떤 순서로 보는지는 `agents.ts` 의 프로필이 정한다 — 이 함수에 남은 일은
610
- * pane 에서 근거(프로세스 이름·제목)를 모으는 것뿐이다.
680
+ * **훅이 말해 준 값이 추측보다 앞선다** (`notify.agentOf`). 훅 스크립트는
681
+ * `-Agent claude|codex|gemini` 로 불리므로 자기가 누구인지 알고 있다. 명령줄(WMI)·탭 제목
682
+ * 추측은 훅이 없는 탭의 폴백이고 흔들린다: npm 전역 래퍼로 띄운 gemini 는 자식이
683
+ * `node.exe` 하나뿐이고 제목에도 이름이 없었다(2026-09-08 실측).
611
684
  *
612
- * `via` 를 함께 돌려주는 이유: 나중에 "왜 codex 로 잡혔나" 를 가르려면 프로세스에서
613
- * 나온 판정인지 제목(신뢰도가 낮다)에서 나온 판정인지가 유일한 단서다.
685
+ * 단 **프로세스 이름이 에이전트를 확실히 가리키면 그것이 훅보다 앞선다.** 훅 값은 보고한
686
+ * 세션이 살아 있을 때만 사실이고, 그 세션이 끝났다고 알려 주는 훅은 없다 — 같은 탭에서
687
+ * claude 를 끄고 훅 없는 codex 를 띄우면 옛 값이 남는다. (자식이 없는 것은 근거로 쓰지 않는다 —
688
+ * 빈 목록은 믿을 만한 종료 신호가 아니다. 그래서 claude 를 끄고 맨 셸로 남은 탭은 다음 훅 보고 전까지 옛 값을 든다.)
689
+ *
690
+ * 순서: 프로세스 이름(확실할 때) → 훅 → 그 pid 의 명령줄(WMI) → 탭 제목. 어느 조각으로
691
+ * 무엇을 알아보는지는 `agents.ts` 의 프로필이 정한다 — 이 함수는 근거를 모으기만 한다.
692
+ *
693
+ * `via` 를 함께 돌려주는 이유: 나중에 "왜 codex 로 잡혔나" 를 가르려면 훅이 말한 것인지
694
+ * 프로세스에서 나온 판정인지 제목(신뢰도가 낮다)에서 나온 판정인지가 유일한 단서다.
614
695
  */
615
696
  private detectAgentApp;
697
+ /**
698
+ * 훅이 고정해 둔 **상태**도 함께 버린다 — 정체(`forgetAgent`)를 버리는 자리에서 같이 부른다.
699
+ *
700
+ * 정체만 버리면 절반이다. 같은 탭에서 claude 를 끄고 다른 CLI 를 띄우면 `hookAgent` 는
701
+ * 지워지지만 `status` 는 여전히 `pinned` 라(`setManual` 이 세운다) 새 CLI 의 출력이
702
+ * `setAuto` 로 아무리 들어와도 전부 무시된다 — 배지가 **끝난 세션의 마지막 상태**(보통
703
+ * `완료`나 `진행중`)에 박혀 있게 된다. 훅이 없는 CLI 로 갈아탔으면 그 탭에는 배지를
704
+ * 되살릴 길이 영영 없다.
705
+ *
706
+ * `unpin` 은 상태를 idle 로 되돌린다 — 지금 그 탭에서 도는 것은 **다른 프로그램**이고,
707
+ * 옛 세션의 상태를 물려받을 이유가 없다. 새 CLI 에도 훅이 있으면 첫 보고가 다시 고정한다.
708
+ */
709
+ private forgetHookStatus;
616
710
  /**
617
711
  * 그 pid 들의 명령줄을 한 줄로 이어 돌려준다 (식별 폴백 전용).
618
712
  *
@@ -649,6 +743,8 @@ export declare class AgentDeckService {
649
743
  * 영영 매칭되지 않는다.
650
744
  */
651
745
  private profileForPane;
746
+ /** `profileForPane` 의 본체 — 캐시를 거치지 않는 실제 판정 */
747
+ private resolveProfileForPane;
652
748
  private profileForTab;
653
749
  /**
654
750
  * pane.sendInput 을 한 번 감싸 PTY 로 나간 입력을 진단 로그에 남긴다.
@@ -728,6 +824,24 @@ export declare class AgentDeckService {
728
824
  private applyOpacity;
729
825
  /** 지금 사이드바가 붙어 있는 방향 (설정값이 망가져 있으면 오른쪽으로 본다) */
730
826
  private get dockSide();
827
+ /**
828
+ * 배치의 기준이 되는 **눈에 보이는** 창 영역 — viewport 와 `.window` 의 교집합.
829
+ *
830
+ * `windowEl.clientWidth` 를 그대로 쓰면 안 된다. `.window` 의 박스가 viewport 보다 넓어져
831
+ * 있는 화면이 실제로 나온다 (2026-09-14 실측: 설정 탭에서 터미널 오른쪽에 100px 쯤 빈 띠가
832
+ * 남고, 사이드바는 그만큼 화면 밖으로 밀려 검색창·줄이 오른쪽에서 잘렸다). 그 폭으로 나누면
833
+ * 사이드바 몫의 일부가 화면 밖에 떨어지고, 화면 안에서는 아무도 안 쓰는 띠로 보인다.
834
+ * 보이는 범위로 잘라 두면 창이 어떤 크기든 그 안에서만 나눈다.
835
+ */
836
+ private visibleBox;
837
+ /**
838
+ * 절대 배치의 기준점 — 컨테이닝 블록(위치 지정 조상의 **패딩 박스**) 왼쪽 위를 viewport 좌표로.
839
+ *
840
+ * `right: 0` 으로 가장자리에 붙이던 것을 실좌표(`left`)로 바꾼 이유가 이것이다 —
841
+ * 그 `0` 이 어느 박스의 오른쪽인지가 화면마다 다르고, 그 박스가 화면보다 넓으면
842
+ * 사이드바가 화면 밖에 앉는다. 기준점을 재서 좌표를 직접 주면 어느 쪽이든 같은 자리다.
843
+ */
844
+ private anchorOrigin;
731
845
  /**
732
846
  * 사이드바가 차지할 크기를 px 로 정한다 — 좌/우면 폭, 상/하면 높이.
733
847
  *
@@ -893,29 +1007,48 @@ export declare class AgentDeckService {
893
1007
  private renderPlan;
894
1008
  private render;
895
1009
  /**
896
- * 그룹 끝에 **지난 세션** 줄을 붙인다 — `⟲ 지난 세션 5` 한 줄(기본 접힘)과, 펴면 그 목록.
1010
+ * 사이드바 하단의 "지금 이 탭" 줄 — 활성 탭에서 도는 모델·계정·컨텍스트%·한도.
897
1011
  *
898
- * 살아 있는 탭 뒤에 오는 이유: 사이드바의 주인공은 지금 도는 작업이고 이건 그 밑의 서랍이다.
899
- * 검색·상태필터가 걸려 있을 때는 아예 그리지 않는다 — 그 둘은 **탭**을 좁히는 장치라
900
- * (`tabMatchesFilter`) 죽은 세션에는 적용되지 않는데, 걸러진 화면에 안 걸러진 줄이 섞이면
901
- * "검색 결과" 라는 말이 깨진다.
1012
+ * **활성 탭 하나만 그린다.** 줄마다 붙이지 않는 이유는 폭이다 — 200px 사이드바에서
1013
+ * 모델 이름과 한도 세 칸은 제목·상태 배지와 자리를 다툰다. 대신 탭을 바꾸면 다시 그려지는데,
1014
+ * 그건 따로 배선할 것도 없다(`activeTabChange$` 가 이미 `scheduleRender` 를 부른다).
1015
+ *
1016
+ * 값이 없으면 **줄을 통째로 접는다**(`hidden`). "알 수 없음" 을 그려 두면 자리만 차지하면서
1017
+ * "statusLine 래퍼가 안 걸렸다" 와 "걸렸는데 아직 첫 보고가 안 왔다" 가 화면에서 같아진다.
902
1018
  */
903
- private renderResume;
1019
+ private renderNow;
904
1020
  /**
905
- * 이 줄 묶음이 덮는 그룹 키들.
1021
+ * 사이드바 바닥의 **지난 세션** 서랍 — `⟲ 지난 세션 128` 한 줄(기본 접힘)과, 펴면 그 목록.
1022
+ *
1023
+ * **목록(`.ad-list`) 밖, "지금 이 탭" 줄 바로 위에 고정된다.**
1024
+ * 예전에는 그룹마다 하나씩 목록 안에 달려 있었는데, 탭이 몇 개만 되어도 서랍이 화면 밖으로
1025
+ * 밀려나 "있는 줄도 몰랐다". 바닥에 붙여 두면 언제나 같은 자리에 있고, 펼치면 위로 자란다
1026
+ * (2026-09-14 유저 지시).
1027
+ *
1028
+ * **줄 수를 자르지 않는다** — 기록해 둔 세션은 전부 들어가고, 서랍은 다섯 줄 남짓만 보이는
1029
+ * 스크롤 상자다 (2026-09-14 유저: "기껏 저장한걸 굳이 안쓸 이유는 없을 듯").
1030
+ * 그러니 `resumeListLimit`·`resumeListDays` 의 기본값은 0(무제한)이다.
906
1031
  *
907
- * 헤더를 그리는 화면에서는 `group.key` 하나지만, **그룹이 하나뿐이면 사이드바는 헤더를
908
- * 안 그리고 목록을 평면 한 덩이로 만든다**(`renderPlan`) — 그때 `group.key` 는 `null`(기타)이
909
- * 되어 어떤 세션도 안 걸린다. 프로젝트를 하나만 열어 둔 화면이 가장 흔하므로 그대로 두면
910
- * 기능이 평소에 안 보인다. 그래서 **그 덩이에 실제로 들어 있는 탭들의 키**를 모아 쓴다.
1032
+ * 검색어가 걸려 있으면 **여기에도 적용한다**. 서랍이 목록 안에 있던 시절에는 검색 중에
1033
+ * 서랍을 통째로 감췄는데(걸러진 화면에 안 걸러진 줄이 섞이면 "검색 결과" 가 깨진다),
1034
+ * 이제는 늘 보이는 자리라 감추면 "검색하면 지난 세션이 사라진다" 가 된다.
911
1035
  */
912
- private resumeKeysOf;
1036
+ private renderResumeDrawer;
1037
+ /** 검색어가 지난 세션 줄에 걸리나 — 탭과 같은 자리(제목·작업 폴더)를 본다 */
1038
+ private resumeMatchesQuery;
913
1039
  /** 지금 탭이 들고 있는 세션 — `세션id -> 탭 식별용 문자열`. 되돌아올 탭은 `resumeTabs` 가 들고 있다 */
914
1040
  private liveSessionIds;
915
1041
  /** 지난 세션 서랍이 펴져 있나. `collapsedGroups` 와 **반대로 펼침을 적는다** — 기본이 접힘이라서 */
916
1042
  private isResumeExpanded;
917
1043
  private toggleResumeExpanded;
918
- private renderResumeHead;
1044
+ /**
1045
+ * 서랍 머리줄을 채운다 (엘리먼트는 사이드바를 만들 때 한 번 만들어 둔 것을 다시 쓴다).
1046
+ *
1047
+ * 머리줄만 `innerHTML` 을 다시 쓰지 않고 자리만 채우는 이유 — 여기에는 클릭 핸들러가
1048
+ * 붙어 있고, 렌더마다 노드를 갈아치우면 핸들러도 매번 다시 걸어야 한다.
1049
+ * 펼침 표시는 `▴`/`▾` 가 아니라 **위로 열린다는 방향** 그대로 쓴다.
1050
+ */
1051
+ private fillResumeHead;
919
1052
  private renderResumeRow;
920
1053
  /**
921
1054
  * 필터에 걸린 것이 하나도 없을 때의 한 줄.
@@ -973,6 +1106,19 @@ export declare class AgentDeckService {
973
1106
  * 동적 텍스트는 textContent 로만 넣는다 (innerHTML 금지).
974
1107
  */
975
1108
  private renderHeadCount;
1109
+ /**
1110
+ * 제목 줄 머리에 에이전트 표식을 붙인다 — 어느 줄이 Claude 고 Codex 고 Gemini 인지
1111
+ * 글자를 읽지 않고 갈리게 (2026-09-14 요청: "뭐가 코덱스고 뭐가 클로드인지 구분이 안돼").
1112
+ *
1113
+ * **제목 텍스트를 건드리지 않는다.** 표식을 제목 문자열에 이어 붙이면 `.ad-title` 의
1114
+ * textContent 가 바뀌어 그것으로 줄을 찾는 회귀 프로브(`tools/probe-resume.js`)가 엉뚱한
1115
+ * 줄을 짚는다. 그래서 **글자가 없는 자식 노드**(빈 span + 인라인 SVG)로만 얹는다 —
1116
+ * 툴팁도 SVG 안 `<title>` 이 아니라 그 span 의 `title` 속성에 건다(같은 이유).
1117
+ *
1118
+ * 못 알아본 탭(보통 셸)에는 아무것도 그리지 않는다. 셋 중 하나를 고를 근거가 없을 때
1119
+ * 기본값을 그리면 "Claude 인 줄 알았는데 셸" 이라는 오독을 만든다.
1120
+ */
1121
+ private decorateAgent;
976
1122
  private renderTab;
977
1123
  /**
978
1124
  * 키보드가 지금 어디를 갖고 있나 — **`document.activeElement` 가 유일한 출처다.**
@@ -1049,6 +1195,23 @@ export declare class AgentDeckService {
1049
1195
  * 헤더에서는 넘기지 않는다 — 접었다 펴 보는 것은 아직 목록을 훑는 중이다.
1050
1196
  */
1051
1197
  private activateNav;
1198
+ /**
1199
+ * `Ctrl-N` — 사이드바에 **보이는 순서**로 N 번째 세션으로 간다 (`pickJumpTarget`).
1200
+ *
1201
+ * ↑↓ 와 달리 훑어보기가 아니라 **바로 전환**이다. 사람이 번호를 누르는 것은 이미 어디로
1202
+ * 갈지 정했다는 뜻이고, 포커스만 옮기면 Enter 를 한 번 더 눌러야 해서 키 두 번짜리
1203
+ * `Ctrl-Shift-L` + ↑↓ 와 다를 것이 없어진다.
1204
+ *
1205
+ * **키보드 내비게이션 모드에 들어가지 않는다.** 목록에 포커스를 주면 그 다음 타이핑이
1206
+ * 터미널로 안 가고(`navMode === 'list'`) 사람은 방금 연 세션에 곧바로 치려던 참이다.
1207
+ * 그래서 `navFocus` 만 그 줄로 맞춰 둔다 — 이어서 `Ctrl-Shift-L` 을 누르면 방금 간 줄에서
1208
+ * 훑기가 시작된다.
1209
+ *
1210
+ * 사이드바를 껐거나(`enabled: false`) 키보드 조작을 끈 사람(`keyboardNav: false`)에게는
1211
+ * 이 키도 없다 — `navReady` 하나로 판정한다. 사이드바가 안 보이는데 "보이는 순서로 N 번째"
1212
+ * 는 말이 되지 않고, 끈 기능의 키만 살아 있으면 터미널이 `Ctrl-N` 을 영영 못 받는다.
1213
+ */
1214
+ private jumpToRow;
1052
1215
  /**
1053
1216
  * Esc — **보이는 것부터 되돌린다.** 걸린 검색어·상태 필터가 있으면 그것을 먼저 지우고
1054
1217
  * 포커스는 사이드바에 남기고, 지울 것이 없을 때 비로소 터미널로 나간다.