@telosmaylx/dsh-session-notify 0.1.8 → 0.1.10
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 +590 -0
- package/README.ja.md +590 -0
- package/README.ko.md +590 -0
- package/README.md +590 -577
- package/README.zh-TW.md +590 -0
- package/lib/client.js +168 -68
- package/lib/core.js +72 -31
- package/lib/index.js +46 -10
- package/package.json +74 -70
package/README.ko.md
ADDED
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# dsh-session-notify
|
|
4
|
+
|
|
5
|
+
[简体中文](README.md) · [English](README.en.md) · [繁體中文](README.zh-TW.md) · [日本語](README.ja.md) · **한국어**
|
|
6
|
+
|
|
7
|
+
**DSH(DeepSeek Harness)세션 완료 알림 플러그인 —— 매 턴이 끝날 때 완료 상태가 스스로 여러분을 찾아갑니다. 화면을 지켜볼 필요가 없습니다.**
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/@telosmaylx/dsh-session-notify)
|
|
10
|
+
[](https://www.npmjs.com/package/@telosmaylx/dsh-session-notify)
|
|
11
|
+
[](./LICENSE)
|
|
12
|
+
[](https://www.npmjs.com/package/@telosmaylx/dsh-session-notify)
|
|
13
|
+
[](https://www.npmjs.com/package/@telosmaylx/dsh-session-notify)
|
|
14
|
+
[](https://github.com/TelosmaYLX/dsh-session-notify/pulls)
|
|
15
|
+
|
|
16
|
+
대화의 각 턴이 끝날 때 「완료 / 오류 / 차단 / 상한 도달」 상태를 소요 시간, token 소모량과 함께 세션 로그에 기록하고, 브라우저 시스템 알림과 페이지 내 toast를 푸시합니다. 5개 언어, 시각적 문구 템플릿 편집기, 커스텀 프리셋 라이브러리가 내장되어 있으며, 캐시 적중률과 생성 속도는 공식 프로젝션에서 가져와 상태 표시줄과 동일한 기준을 사용합니다.
|
|
17
|
+
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 목차
|
|
23
|
+
|
|
24
|
+
- [주요 기능](#주요-기능)
|
|
25
|
+
- [환경 요구사항](#환경-요구사항)
|
|
26
|
+
- [설치](#설치)
|
|
27
|
+
- [제거](#제거)
|
|
28
|
+
- [빠른 시작](#빠른-시작)
|
|
29
|
+
- [알림 동작](#알림-동작)
|
|
30
|
+
- [트리거 조건](#트리거-조건)
|
|
31
|
+
- [알림 본문은 어디서 오나](#알림-본문은-어디서-오나)
|
|
32
|
+
- [알림 예시](#알림-예시)
|
|
33
|
+
- [알림 권한](#알림-권한)
|
|
34
|
+
- [설정](#설정)
|
|
35
|
+
- [설정 패널](#설정-패널)
|
|
36
|
+
- [문구 템플릿과 플레이스홀더](#문구-템플릿과-플레이스홀더)
|
|
37
|
+
- [프리셋 시스템](#프리셋-시스템)
|
|
38
|
+
- [호스트 설정 항목](#호스트-설정-항목)
|
|
39
|
+
- [동작 원리](#동작-원리)
|
|
40
|
+
- [프로젝트 구조](#프로젝트-구조)
|
|
41
|
+
- [개발 및 디버깅](#개발-및-디버깅)
|
|
42
|
+
- [자주 묻는 질문](#자주-묻는-질문)
|
|
43
|
+
- [변경 로그](#변경-로그)
|
|
44
|
+
- [기여](#기여)
|
|
45
|
+
- [관련 링크](#관련-링크)
|
|
46
|
+
- [라이선스](#라이선스)
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 주요 기능
|
|
51
|
+
|
|
52
|
+
### 3채널 알림, 하나도 빠짐없이
|
|
53
|
+
|
|
54
|
+
| 채널 | 형태 | 설명 |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| 세션 내 시스템 메시지 | 접을 수 있는 알림 행 | 각 턴이 끝날 때 종료 사유와 소요 시간, 소모량을 플러그인 소스의 시스템 메시지로 세션 로그에 추가합니다. JSONL로 디스크에 저장되며 세션을 복원하거나 재생해도 계속 볼 수 있습니다. |
|
|
57
|
+
| 브라우저 시스템 알림 | Web Notification | 네이티브 팝업입니다. 각 완료 이벤트는 고유한 `tag`(`dsh-session-notify:<timestamp>`)를 사용하므로 이전 알림과 서로 교체되지 않고 그룹 항목으로 접히지 않습니다. 알림을 클릭하면 창으로 포커스가 돌아옵니다. |
|
|
58
|
+
| 페이지 내 toast | 오른쪽 아래 플로팅 팝업 | 항상 표시되는 보험 채널입니다. 시스템 알림이 플랫폼에서 무음 처리되거나 권한이 거부되거나 환경이 지원하지 않아도 시각적 피드백이 남아 있습니다. 같은 화면에 최대 3개(초과 시 가장 오래된 것 제거), 10초 후 자동으로 사라지며 클릭하면 닫힙니다. |
|
|
59
|
+
|
|
60
|
+
### 백그라운드 세션 완전 지원
|
|
61
|
+
|
|
62
|
+
- 호스트는 모든 세션(백그라운드, 열려 있지 않은 창 포함)에 대해 「마지막 알림 본문」의 세션 프로젝션 유닛(key = `session-complete-notify`)을 유지합니다. 알림 본문은 세션 간에 일관되며, 사용자가 해당 창을 열어 두고 있지 않아도 됩니다.
|
|
63
|
+
- 클라이언트는 세션 목록 스냅샷에서 모든 세션의 `running` 비트를 관찰하며, `true → false` 엣지가 곧 알림 트리거입니다. 공식 sidebar 알림과 동일한 전략입니다(최초 관찰 시에는 기준선만 기록하며, 이미 idle 상태인 세션은 추가 발송하지 않습니다).
|
|
64
|
+
|
|
65
|
+
### 문장 하나하나까지 커스터마이징
|
|
66
|
+
|
|
67
|
+
- **5개 언어**: 간체 중국어, 번체 중국어, English, 일본어, 한국어 —— 알림 문구, 소요 시간·소모량 표현, 설정 패널 UI가 모두 언어에 따라 전환됩니다(전환 즉시 재렌더링).
|
|
68
|
+
- **시각적 템플릿 편집기**(Chip 캡슐 편집기): 동적 정보를 인라인 캡슐로 렌더링합니다(플레이스홀더 코드가 노출되지 않음). 「+ 정보 삽입」은 커서 위치에 삽입되며(텍스트 중간에도 삽입 가능), 캡슐을 클릭하면 제거됩니다. 각 항목마다 실시간 미리보기가 제공됩니다(정보는 예시 값으로 본문에 흘러 들어감).
|
|
69
|
+
- **프리셋 시스템**: 기준선으로 내장된 「기본」 프리셋이 있습니다. 현재 설정은 커스텀 프리셋으로 별도 저장할 수 있으며(`localStorage` 영속화), 자동 번호가 매겨진 이름 없는 프리셋(`이름 없음`, `이름 없음 2`…)과 「출처: xxx · 수정됨」 출처 표시, 프리셋 삭제를 지원합니다.
|
|
70
|
+
- **알림 제목 템플릿**: 비워 두면 각 사유에 대해 기본 제목을 사용합니다(완료=작업 완료 / 오류=작업 오류 / …). `{title}`은 세션 제목을 참조합니다.
|
|
71
|
+
|
|
72
|
+
### 공식 지표와 동일 기준
|
|
73
|
+
|
|
74
|
+
- **캐시 적중률**은 공식 `tokenUsage` 프로젝션에서 가져옵니다: 캐시 읽기 /(비캐시 입력 + 캐시 읽기 + 캐시 쓰기).
|
|
75
|
+
- **생성 속도**는 공식 `sessionStats` 프로젝션에서 가져옵니다: 출력 token ÷ 디코딩 소요 시간.
|
|
76
|
+
- 두 지표 모두 dsh-web-ui 상태 표시줄과 완전히 동일한 기준이며, 대기, 준비, 도구 시간은 포함하지 않습니다. 프로젝션을 사용할 수 없거나 데이터가 준비되지 않으면 로컬 사용량 집계 추정으로 자동 폴백합니다.
|
|
77
|
+
|
|
78
|
+
> [!NOTE]
|
|
79
|
+
> 캐시 적중률과 속도는 커스텀 템플릿에서 `{cache}`, `{tps}` 플레이스홀더로 삽입할 때만 표시됩니다. 내장 기본 문구를 사용할 때는 본문에 소요 시간과 소모량만 포함됩니다.
|
|
80
|
+
|
|
81
|
+
### 엔지니어링 품질
|
|
82
|
+
|
|
83
|
+
- **실시간 이벤트에만 응답**: resume, replay 시 이전 알림을 재생하지 않으며, 세션을 불러와도 화면이 넘치지 않습니다.
|
|
84
|
+
- **자체 무한 루프 방지**: 플러그인이 추가하는 메시지 유형(`user/message`)과 자체 리스닝 대상(`turn/*`)이 서로 겹치지 않습니다.
|
|
85
|
+
- **외부 의존성 제로**: 호스트 플레인에는 bare import가 없으며, UserMessage는 `dsh-llm`의 `createUserMessage` 계약에 따라 수동으로 구성합니다. 순수 로직 레이어(`lib/core.js`)는 의존성이 없어 독립적으로 테스트할 수 있습니다.
|
|
86
|
+
- **Cordis effect 규율**: 재시도 타이머를 `ctx.effect()`에 감싸 `clearTimeout` disposer를 반환하며, 등록은 fiber 언마운트 시 자동으로 취소되어 HMR 핫 리로드에도 안전합니다.
|
|
87
|
+
- **설치 즉시 마운트**: 공식 `dsh.bundle` manifest를 선언하므로 `dsh plugin add` 한 줄이면 설치 후 바로 사용할 수 있으며, patch를 직접 작성할 필요가 없습니다.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 환경 요구사항
|
|
92
|
+
|
|
93
|
+
| 의존성 | 요구사항 |
|
|
94
|
+
| --- | --- |
|
|
95
|
+
| DSH(DeepSeek Harness) | Web profile 배포. 공식 base bundle에는 기본적으로 `@deepseek-ai/dsh-settings`(설정 네임스페이스)와 세션 프로젝션이 포함되어 있어 추가 설정이 필요 없습니다 |
|
|
96
|
+
| cordis | `>=4.0.0-rc <5`(peer dependency, 호스트가 제공) |
|
|
97
|
+
| Node.js | `>=22`(호스트 측) |
|
|
98
|
+
| 브라우저 | Web Notification을 지원하면 시스템 알림 사용 가능. 미지원, 권한 거부, 무음 처리 시 toast가 폴백 |
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 설치
|
|
103
|
+
|
|
104
|
+
> [!WARNING]
|
|
105
|
+
> 맨 `npm install`은 패키지를 의존성 트리에 넣을 뿐 **플러그인을 등록하지 않습니다** —— 이는 DSH의 공식 설계입니다(`npm install only adds the dependency; it does not register the plugin`). 자동 마운트의 유일한 공식 경로는 `dsh plugin add`입니다: 패키지 내 `dsh.bundle` manifest(이 플러그인은 0.1.3부터 선언하며, 저장소 루트의 `cordis.patch.yml`을 가리킴)를 읽어 자동 적용합니다.
|
|
106
|
+
|
|
107
|
+
### 방법 1: dsh plugin add(권장)
|
|
108
|
+
|
|
109
|
+
패키지 설치와 동시에 `cordis.patch.yml`을 자동 적용하여 플러그인을 profile 어셈블리에 마운트합니다(host 이벤트 구독 + client 시작 그래프 주입).
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
dsh plugin --profile web add @telosmaylx/dsh-session-notify
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 방법 2: GitHub 저장소에서 설치
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
dsh plugin add github:TelosmaYLX/dsh-session-notify
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
DSH Web GUI 세션 안에서도 실행할 수 있습니다:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
dev_install_package github=TelosmaYLX/dsh-session-notify
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 방법 3: 로컬 디렉터리 핫 어셈블(개발용)
|
|
128
|
+
|
|
129
|
+
경로를 사용자의 클론 디렉터리로 바꾼 뒤 DSH Web GUI 세션 안에서 실행합니다:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
dev_install_package dir=/你的/克隆目录/dsh-session-notify
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 방법 4: npm 패키지 수동 설치
|
|
136
|
+
|
|
137
|
+
먼저 패키징합니다:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npm pack @telosmaylx/dsh-session-notify
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
압축 해제 후 지정 디렉터리에 설치합니다(DSH Web GUI 세션 안에서 실행):
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
dev_install_package dir=/解压/目录/package
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 방법 5: 수동 cordis patch(설치기 불필요)
|
|
150
|
+
|
|
151
|
+
`~/.dsh/profiles/web/cordis.patch.yml`에 추가합니다:
|
|
152
|
+
|
|
153
|
+
```yaml
|
|
154
|
+
- insert:
|
|
155
|
+
- id: dsh-session-notify
|
|
156
|
+
name: '@telosmaylx/dsh-session-notify'
|
|
157
|
+
config: {}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
> [!IMPORTANT]
|
|
161
|
+
> 어떤 방법을 쓰든 설치 후에는 **브라우저 페이지를 한 번 새로고침**해야 합니다 —— 클라이언트 bundle은 `__DSH_BOOT__` 시작 그래프를 통해 주입됩니다.
|
|
162
|
+
|
|
163
|
+
## 제거
|
|
164
|
+
|
|
165
|
+
한 줄 명령으로 플러그인과 그 마운트를 제거합니다(`cordis.patch.yml`에서 insert 항목을 자동으로 제거):
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
dsh plugin --profile web remove @telosmaylx/dsh-session-notify
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
> [!NOTE]
|
|
172
|
+
> 수동 설치(방법 4/5) 사용자는 `~/.dsh/profiles/web/cordis.patch.yml`에서 해당 insert 항목을 함께 삭제한 뒤 페이지를 새로고침해야 합니다.
|
|
173
|
+
|
|
174
|
+
### 제거 시 자동으로 정리되는 항목
|
|
175
|
+
|
|
176
|
+
플러그인은 완전한 생명주기 마무리를 구현합니다(Cordis effect 규율). 제거/비활성화/HMR 핫 리로드 시:
|
|
177
|
+
|
|
178
|
+
| 플레인 | 자동 해제되는 리소스 |
|
|
179
|
+
| --- | --- |
|
|
180
|
+
| host | `session/event` 이벤트 구독, settings 네임스페이스, 세션 프로젝션 유닛, 설정 등록 재시도 타이머(`ctx.effect` 래핑). 언로드 플래그를 설정해 예약된 마이크로태스크 추가를 억제 |
|
|
181
|
+
| client | 세션 목록 구독, 완료 알림 본문 폴링 타이머, `window.__dsch_notify_debug` 디버그 훅(참조로 삭제, 클로저 누수 방지), 페이지 내 toast 컨테이너 DOM |
|
|
182
|
+
|
|
183
|
+
### 제거 후 유지되는 데이터
|
|
184
|
+
|
|
185
|
+
- **설정 구성**(언어, 문구 템플릿)은 settings 문서에 남아 있어 재설치 후 자동으로 복원됩니다.
|
|
186
|
+
- **커스텀 프리셋**은 브라우저 `localStorage`(`dsh-scn-custom-presets`)에 저장되므로 재설치 후에도 유지됩니다.
|
|
187
|
+
- 과거 세션에 추가된 시스템 메시지와 JSONL 로그는 **롤백되지 않습니다**(세션 데이터의 일부이며, 공식 사이드바 알림과 동일한 의미).
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 빠른 시작
|
|
192
|
+
|
|
193
|
+
1. 위의 아무 방법으로나 설치하고 페이지를 새로고침합니다.
|
|
194
|
+
2. 아무 대화 턴이나 시작하고 끝나기를 기다립니다 —— 오른쪽 아래에 toast가 뜨고, 브라우저에 시스템 알림이 뜨며, 세션 로그에 접을 수 있는 시스템 알림 행이 나타납니다.
|
|
195
|
+
3. 처음으로 완료 이벤트를 받으면 브라우저가 알림 권한을 요청합니다(페이지당 한 번만 물어봄). 허용하면 이후 완료마다 시스템 알림이 표시됩니다.
|
|
196
|
+
4. **설정 → 플러그인 → 세션 완료 알림**을 열어 언어를 전환하고, 문구 템플릿을 편집하고, 프리셋을 별도로 저장합니다. 저장 후 「클릭하여 새로고침」을 누르면 호스트와 클라이언트 양쪽이 다시 읽어 새 설정이 적용됩니다.
|
|
197
|
+
|
|
198
|
+
방금 설치한 직후에는 세션 로그에 다음과 같은 접을 수 있는 알림 행이 나타납니다:
|
|
199
|
+
|
|
200
|
+
```text
|
|
201
|
+
会话「重构登录模块」已完成(用时 1 分 12 秒,消耗 1,240 输入 / 3,560 输出)。
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
> 기본 문구는 「세션」 뒤에 세션 제목 라벨(`{title}`)을 내장합니다. 세션에 제목이 없으면 「세션 완료」로 자동 폴백합니다.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## 알림 동작
|
|
209
|
+
|
|
210
|
+
### 트리거 조건
|
|
211
|
+
|
|
212
|
+
대화의 각 턴이 끝날 때(`turn/end`) 종료 사유를 판단하여 화이트리스트에 포함되면 알림을 보냅니다:
|
|
213
|
+
|
|
214
|
+
| 종료 사유 | 의미 | 기본값 |
|
|
215
|
+
| --- | --- | --- |
|
|
216
|
+
| `completed` | 세션 정상 완료 | 알림 |
|
|
217
|
+
| `aborted` | 세션 중단 | 알림 |
|
|
218
|
+
| `blocked` | 세션 차단됨 | 알림 |
|
|
219
|
+
| `error` | 세션 오류(오류 상세 포함, 초과 시 잘림) | 알림 |
|
|
220
|
+
| `max-tokens` | 출력 token 상한 도달 | 알림 |
|
|
221
|
+
| `interrupted` | 중단(크래시 복구 후 영속화 백엔드가 보완한 고아 턴 종료 표시) | 알림 없음(설정으로 추가 가능) |
|
|
222
|
+
|
|
223
|
+
**하위 에이전트 세션은 기본적으로 건너뜁니다**(`header.origin === 'subagent'` 또는 `delegationDepth > 0`) —— 하위 에이전트는 상위 세션이 조율하므로 턴마다 알림을 보내면 노이즈가 됩니다. 호스트 설정에서 건너뛰기를 해제할 수 있습니다.
|
|
224
|
+
|
|
225
|
+
### 알림 본문은 어디서 오나
|
|
226
|
+
|
|
227
|
+
클라이언트는 세션 목록에서 `running: true → false` 엣지를 관찰하면 알림을 보내며, 본문은 다음 우선순위로 가져옵니다(최대 6초 폴링, 400ms 간격):
|
|
228
|
+
|
|
229
|
+
1. **호스트 프로젝션**(key = `session-complete-notify`) —— 모든 세션에 있으며, 백그라운드 세션도 동일하게 전문을 받습니다.
|
|
230
|
+
2. **세션 이벤트 윈도우의 notice 노드**(`kind=context` + `form=notice`) —— 보고 있는 세션은 디스크 저장 직후 바로 사용할 수 있습니다.
|
|
231
|
+
3. **폴백** —— 「상세는 세션 내 시스템 메시지 참조」+ 작업 영역 정보(`cwd` 마지막 세그먼트).
|
|
232
|
+
|
|
233
|
+
### 알림 예시
|
|
234
|
+
|
|
235
|
+
아래는 모두 `lib/core.js`의 `buildNotice`가 실제로 생성한 것입니다. 기본 문구는 종료 사유에 따라 **차별화된 표현**을 사용합니다(단일 문형이 아님):
|
|
236
|
+
|
|
237
|
+
간체 중국어 기본 문구:
|
|
238
|
+
|
|
239
|
+
```text
|
|
240
|
+
会话「重构登录模块」已完成(用时 3 分 25 秒,消耗 12,400 输入 / 35,600 输出)。 ← 完成:括号紧凑式 + 内嵌会话标题
|
|
241
|
+
会话「重构登录模块」已中止。用时 3 分 25 秒,消耗 12,400 输入 / 35,600 输出。 ← 中止:句号拆句
|
|
242
|
+
会话「重构登录模块」被阻塞。用时 3 分 25 秒,消耗 12,400 输入 / 35,600 输出。 ← 阻塞:句号拆句
|
|
243
|
+
会话「重构登录模块」达到输出上限。用时 3 分 25 秒,消耗 12,400 输入 / 35,600 输出,建议拆分任务后重试。 ← 上限:附建议
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
> 세션에 제목이 없으면(`titleValue`가 비어 있음) 제목 없는 문형으로 자동 폴백합니다(예: 「세션 완료(소요 …)」).
|
|
247
|
+
|
|
248
|
+
오류 발생 시 오류 상세가 앞에 표시됩니다(한 줄로 정리, 40자 초과 시 잘림):
|
|
249
|
+
|
|
250
|
+
```text
|
|
251
|
+
会话「重构登录模块」出错:connection timeout(用时 12 秒)。
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
English 기본 문구(세션 제목은 큰따옴표 사용):
|
|
255
|
+
|
|
256
|
+
```text
|
|
257
|
+
Session "重构登录模块" completed (took 3m25s, used 12,400 in / 35,600 out).
|
|
258
|
+
Session "重构登录模块" hit the output-token cap. Took 3m25s, used 12,400 in / 35,600 out — consider splitting the task.
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
커스텀 템플릿(설정 패널에서 편집하며, 이 예시는 모든 정보 슬롯 사용):
|
|
262
|
+
|
|
263
|
+
```text
|
|
264
|
+
{title} 干完了!用时 {duration},消耗 {usage},缓存命中 {cache},速度 {tps}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
렌더링 결과:
|
|
268
|
+
|
|
269
|
+
```text
|
|
270
|
+
重构登录模块 干完了!用时 3 分 25 秒,消耗 103,600 输入 / 35,600 输出,缓存命中 96.5%,速度 92 tok/s
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
5개 언어의 동일한 이벤트:
|
|
274
|
+
|
|
275
|
+
```text
|
|
276
|
+
会话「重构登录模块」已完成(用时 3 分 25 秒,消耗 1,240 输入 / 3,560 输出)。
|
|
277
|
+
會話「重構登入模組」已完成(用時 3 分 25 秒,消耗 1,240 輸入 / 3,560 輸出)。
|
|
278
|
+
Session "重构登录模块" completed (took 3m25s, used 1,240 in / 3,560 out).
|
|
279
|
+
セッション「重构登录模块」完了(所要 3 分 25 秒、消費 1,240 入力 / 3,560 出力)。
|
|
280
|
+
세션「重构登录模块」 완료(소요 3분 25초, 소모 1,240 입력 / 3,560 출력)。
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### 알림 권한
|
|
284
|
+
|
|
285
|
+
| 권한 상태 | 동작 |
|
|
286
|
+
| --- | --- |
|
|
287
|
+
| `default`(미결정) | 완료 이벤트는 toast만 표시. 설정 패널 「알림 권한」 영역에 「권한 요청」 버튼 제공(**사용자 제스처 내에서 요청** —— Chromium은 제스처가 아닌 자동 요청을 무시하므로 플러그인은 자동 요청하지 않음) |
|
|
288
|
+
| `granted` | 「알림 방식」에 따라 시스템 알림 전송(독립 tag, 서로 덮어쓰지 않음) |
|
|
289
|
+
| `denied`(브라우저가 차단) | toast만. 설정 패널에 주소창 조작 안내 표시(권한 아이콘 → 사이트 설정 → 알림 → 허용) |
|
|
290
|
+
| `undefined`(비보안 컨텍스트 / 미지원) | toast만. 「페이지 내 알림만」으로 전환 권장 |
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## 설정
|
|
295
|
+
|
|
296
|
+
대부분의 설정은 **DSH Web UI → 설정 → 플러그인 → 세션 완료 알림** 패널에서 완료됩니다(저장 후 「클릭하여 새로고침」을 누르면 적용). 「트리거 사유 화이트리스트」와 「하위 에이전트 건너뛰기」 두 항목만 호스트 `cordis.patch.yml`의 `config`에서 설정합니다.
|
|
297
|
+
|
|
298
|
+
### 설정 패널
|
|
299
|
+
|
|
300
|
+
패널은 공식 「설정 → 플러그인」 패널에 등록되며(`settings.plugin.item` keyed slot, key = `session-complete-notify`), 스타일은 네이티브 플러그인 카드를 값 단위로 재현합니다(12px 라운드 코너, 펼치기/접기, 회전 chevron, footer 상태 표시 + 폐기 ghost + 메인 컬러 저장 버튼):
|
|
301
|
+
|
|
302
|
+
| 영역 | 내용 |
|
|
303
|
+
| --- | --- |
|
|
304
|
+
| 프리셋 | 드롭다운으로 내장 또는 커스텀 프리셋 선택. 「추가」는 현재 설정을 커스텀 프리셋으로 별도 저장. 현재 프리셋은 「삭제」 가능 |
|
|
305
|
+
| 언어 | 5개 언어 중 단일 선택, 전환 시 패널 전체 즉시 재렌더링 |
|
|
306
|
+
| 알림 방식 | 3택1: 이중 채널(시스템 알림 + 페이지 내 알림, 기본값) / 시스템 알림만 / 페이지 내 알림만 |
|
|
307
|
+
| 알림 제목 | 모든 사유가 공유하는 제목 템플릿(일반 입력 상자, 네이티브 placeholder 안내: 입력하면 사라지고, 비우면 복원). 비워 두면 각 사유별 기본 제목 사용(완료=작업 완료, 오류=작업 오류, 중단=작업 중단, 차단=작업 차단, 상한=작업이 출력 상한 도달). `{title}`은 세션 제목 참조(「+ 세션 제목」을 클릭해 커서 위치에 삽입) |
|
|
308
|
+
| 템플릿 × 5 | 각 종료 사유(완료, 오류, 중단, 차단, 출력 상한)마다 별도의 Chip 편집기: 텍스트 + 인라인 정보 캡슐, 커서 위치에 삽입, 클릭으로 제거, 실시간 미리보기 |
|
|
309
|
+
| 하위 에이전트 세션 건너뛰기 | 체크박스(저장 시 설정 문서에 함께 기록) |
|
|
310
|
+
| 알림 권한 | 상태 실시간 표시: 승인됨(초록) / 미승인(「권한 요청」 버튼 포함) / 브라우저가 차단함(주소창 조작 안내 포함) / 환경 미지원 |
|
|
311
|
+
| 사유별 제목 커스터마이징 | 접는 영역(기본 접힘): 각 종료 사유마다 독립 제목 입력 상자. 비워 두면 = 전역 템플릿 또는 언어 기본 제목 사용 |
|
|
312
|
+
| 저장 | 호스트 설정 문서에 기록(`language` / `templates` / `titleTemplate` / `titleTemplates` / `pushMode`). 저장 후 「클릭하여 새로고침」 링크 표시 |
|
|
313
|
+
| 초기화 | 원클릭으로 기본값 복원(**언어는 현재 선택 유지**, 제목/템플릿/알림 방식은 기본값으로 복원) 후 즉시 저장 |
|
|
314
|
+
|
|
315
|
+
> [!NOTE]
|
|
316
|
+
> 「알림 방식」의 장단점: `dual`(기본값)은 Windows 시스템 알림과 페이지 내 toast를 동시에 띄우며, toast는 시스템 알림이 플랫폼에서 무음 처리되는 경우(집중 지원, 알림 배너 끄기)를 대비한 보험 채널입니다. 하지만 **QQ 브라우저 등 중국산 Chromium 셸 브라우저는 `Notification`을 「브라우저 내장의 페이지 내 푸시 팝업」으로 렌더링합니다**(페이지 상단/모서리 배너, Windows 알림 센터를 거치지 않음) —— 이 경우 `dual`은 페이지 내에 두 개의 알림이 생깁니다(브라우저 내장 팝업 + 플러그인 toast). 이런 브라우저에서는 「페이지 내 알림만」을 선택하세요(`Notification`을 호출하지 않으므로 브라우저 내장 팝업이 나타나지 않고, 페이지 내에는 플러그인 고유의 작은 toast만 표시됨). 「시스템 알림만」 모드는 QQ 브라우저에서 무효입니다(항상 페이지 내 팝업으로 렌더링됨). 설정 패널의 각 사유별 「전송」 테스트 버튼도 이 영향을 받습니다.
|
|
317
|
+
|
|
318
|
+
> [!NOTE]
|
|
319
|
+
> 시스템 알림(`Notification` API) 표시 여부는 **브라우저와 사이트 접근 방식**이 함께 결정합니다: Edge/Chrome은 "익숙하지 않은" 사이트에 대해 **알림을 자동 차단**합니다(주소창에 「알림 차단됨」 표시) —— 주소창 왼쪽 권한 아이콘 클릭 → 사이트 설정 → 알림 → 허용하면 복구됩니다. `http://IP` 같은 비보안 컨텍스트 접근 시 `Notification`이 아예 존재하지 않으므로 「페이지 내 알림만」으로 전환하세요. 설정 패널의 「알림 권한」 영역에서 현재 상태를 실시간 표시하고 해당 조작 안내를 제공합니다(원클릭 권한 요청 가능). Firefox는 창이 포커스된 상태에서 알림이 페이지 내 배너로 표시되고, 포커스를 잃어야 시스템 알림 센터로 들어갑니다.
|
|
320
|
+
|
|
321
|
+
> [!NOTE]
|
|
322
|
+
> 패널의 「하위 에이전트 세션 건너뛰기」는 설정 문서의 boolean 값으로 저장됩니다. 호스트 `cordis.patch.yml`의 `config.skipSubagents`는 시작 시 기본값이며, 둘 중 하나라도 참이면 건너뜁니다.
|
|
323
|
+
|
|
324
|
+
### 문구 템플릿과 플레이스홀더
|
|
325
|
+
|
|
326
|
+
각 종료 사유마다 독립적인 템플릿 입력 상자가 있으며, **라벨이 곧 스위치**입니다 —— 템플릿에 해당 정보 라벨을 삽입해야 그 데이터가 표시됩니다:
|
|
327
|
+
|
|
328
|
+
| 플레이스홀더 | 의미 | 예시 값 |
|
|
329
|
+
| --- | --- | --- |
|
|
330
|
+
| `{title}` | 세션 제목(알림 제목 템플릿에서도 사용 가능) | `重构登录模块` |
|
|
331
|
+
| `{duration}` | 이번 턴 소요 시간(`turn/start` 시작 → `turn/end` 종료) | `3 分 25 秒` / `3m25s` |
|
|
332
|
+
| `{usage}` | token 소모(입력 = 비캐시 + 캐시 읽기 + 캐시 쓰기) | `1,240 输入 / 3,560 输出` |
|
|
333
|
+
| `{error}` | 오류 정보(오류 없으면 `none` 표시. 한 줄로 정리, 80자 잘림) | `connection timeout` |
|
|
334
|
+
| `{cache}` | 캐시 적중률(공식 프로젝션 기준, 데이터 없으면 비어 있음) | `96.5%` |
|
|
335
|
+
| `{tps}` | 생성 속도(공식 프로젝션 기준, 데이터 없으면 비어 있음) | `92 tok/s` |
|
|
336
|
+
| `{label}` | 폐기됨 —— 렌더링 시 자동 제거, 기존 템플릿은 계속 호환(삽입 메뉴에서 해당 옵션 제거됨) | — |
|
|
337
|
+
|
|
338
|
+
템플릿을 비워 두면 내장 기본 문구를 사용합니다(소요 시간과 소모량 자동 포함). 접히는 행의 `summary`는 본문과 동일한 소스입니다(렌더링 결과 120자로 잘림) —— 접힌 행만 보는 사용자도 실제 제목과 소요 시간, 소모량을 확인할 수 있습니다.
|
|
339
|
+
|
|
340
|
+
### 프리셋 시스템
|
|
341
|
+
|
|
342
|
+
- **내장 프리셋**: 「기본」 하나뿐이며 기준선 역할을 합니다.
|
|
343
|
+
- **커스텀 프리셋**: `localStorage`(key = `dsh-scn-custom-presets`)에 저장됩니다:
|
|
344
|
+
- 「추가」로 이름을 지은 뒤 커스텀 프리셋으로 저장. 저장 후 「수정」으로 자동 동기화, 「삭제」로 제거 가능.
|
|
345
|
+
- **자동 번호가 매겨진 이름 없는 프리셋**: 「기본 / 비어 있음」에서 바로 저장하면 `이름 없음`, `이름 없음 2`, `이름 없음 3`…이 자동 생성됩니다(번호는 현재 최댓값 + 1).
|
|
346
|
+
- 폼에 「출처: xxx · 수정됨」 출처 표시가 나타납니다(프리셋에서 왔지만 내용이 변경된 경우).
|
|
347
|
+
- **저장 즉시 동기화**: 저장 시 폼의 출처가 커스텀 프리셋이면 해당 프리셋을 갱신하고, 그렇지 않으면 새로 만들거나 이름 없는 프리셋 번호를 계속 매깁니다.
|
|
348
|
+
|
|
349
|
+
### 호스트 설정 항목
|
|
350
|
+
|
|
351
|
+
```yaml
|
|
352
|
+
- insert:
|
|
353
|
+
- id: dsh-session-notify
|
|
354
|
+
name: '@telosmaylx/dsh-session-notify'
|
|
355
|
+
config:
|
|
356
|
+
reasons: [completed, aborted, blocked, error, max-tokens]
|
|
357
|
+
skipSubagents: true
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
| 필드 | 타입 | 기본값 | 설명 |
|
|
361
|
+
| --- | --- | --- | --- |
|
|
362
|
+
| `reasons` | `string[]` | `[completed, aborted, blocked, error, max-tokens]` | 알림을 트리거하는 `turn/end` 사유 화이트리스트 |
|
|
363
|
+
| `skipSubagents` | `boolean` | `true` | 하위 에이전트 세션 건너뛰기(`origin=subagent` 또는 `delegationDepth>0`) |
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## 동작 원리
|
|
368
|
+
|
|
369
|
+
플러그인은 **호스트 플레인**(Node)과 **클라이언트 플레인**(브라우저)으로 나뉘며, 사이는 세션 로그(JSONL)와 공식 세션 프로젝션으로 이어집니다:
|
|
370
|
+
|
|
371
|
+
```text
|
|
372
|
+
┌─────────────────── 宿主平面(lib/index.js,Node)──────────────────┐
|
|
373
|
+
│ │
|
|
374
|
+
│ session/event 火线 │
|
|
375
|
+
│ ├─ turn/start → tracker 起表(key: sessionId:turn) │
|
|
376
|
+
│ ├─ assistant/message → 累加该轮 token 用量 │
|
|
377
|
+
│ └─ turn/end → reason.kind ∈ reasons ? │
|
|
378
|
+
│ ├─ 子代理会话?跳过 │
|
|
379
|
+
│ ├─ 读官方投影:cache / tps / title │
|
|
380
|
+
│ ├─ 按语言+模板构建通知(summary ≤120 字) │
|
|
381
|
+
│ └─ queueMicrotask 追加系统消息 │
|
|
382
|
+
│ (避开 append 重入窗口) │
|
|
383
|
+
│ │
|
|
384
|
+
│ settings.register → 官方「设置 → 插件」命名空间(失败退避重试) │
|
|
385
|
+
│ sessionProjections → 注册投影单元(key=session-complete-notify) │
|
|
386
|
+
└──────────────────────────────┬──────────────────────────────────────┘
|
|
387
|
+
│ user/message (source: plugin, form: notice)
|
|
388
|
+
▼ JSONL 持久化 + 投影推送
|
|
389
|
+
┌─────────────────── 客户端平面(lib/client.js,浏览器)──────────────┐
|
|
390
|
+
│ │
|
|
391
|
+
│ 会话列表订阅:running true → false 边沿 → pushCompletion │
|
|
392
|
+
│ ├─ 取正文:投影 → 事件窗口 notice → 降级(轮询 ≤6s) │
|
|
393
|
+
│ ├─ Web Notification(独立 tag,点击聚焦) │
|
|
394
|
+
│ └─ 页内 toast(永远展示,≤3 条,10s 自动消失) │
|
|
395
|
+
│ │
|
|
396
|
+
│ slots.inject('settings.plugin.item') → 设置卡片(预设/语言/模板) │
|
|
397
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### 핵심 설계 결정
|
|
401
|
+
|
|
402
|
+
- **재생 없음**: 실시간 이벤트만 처리하며, resume, replay는 과거 알림을 다시 보내지 않습니다.
|
|
403
|
+
- **자기 순환 없음**: 플러그인은 `user/message`를 추가하고 자체적으로 `turn/*`만 리슨하므로 이벤트 유형이 겹치지 않습니다.
|
|
404
|
+
- **외부 import 제로**: 플러그인은 저장소 디렉터리에서 realpath로 로드되며, `@deepseek-ai/*`는 bare로 해석할 수 없습니다 —— 호스트 플레인은 `createRequire`로 profile 공유 의존성 허브(`.dsh/profiles/node_modules`)를 고정해 `schemastery`(설정 schema)와 `zod`(프로젝션 schema)를 가져옵니다. UserMessage는 `dsh-llm` 계약에 따라 수동으로 구성됩니다(`id = crypto.randomUUID()`, deep-freeze는 `session.append`의 adopt 스냅샷 단계에서 완료).
|
|
405
|
+
- **append 재진입 회피**: `session/event` 관찰자 콜백은 `turn/end`의 그 append 게시 경계 안에서 실행됩니다(dsh-session은 dispatch 전에 `entry.appending`을 설정하고 `finally`에서 리셋). 동기 append는 거부됩니다 —— 따라서 `queueMicrotask`로 지연합니다(마이크로태스크는 이번 동기 스택이 `finally` 리셋을 포함해 끝난 뒤에 실행됨).
|
|
406
|
+
- **effect 규율**: 설정 등록의 백오프 재시도 타이머를 `ctx.effect()`에 감싸 `clearTimeout` disposer를 반환합니다 —— 플러그인이 재시도 창 안에서 언로드되거나 핫 리로드되면 타이머가 fiber와 함께 제거되어 해제된 ctx에 대해 등록을 트리거하지 않습니다(아주 오래된 환경에 `ctx.effect` API가 없으면 일반 타이머 + ctx 제거 시 폴백 캐치로 대체).
|
|
407
|
+
- **HMR 안전**: `core.js` import에 `?v=1` 캐시 버스팅을 붙입니다(HMR 리로드는 URL 키 기준). 설정 등록 시 핫 리로드 경합(duplicate)을 만나면 자동 백오프 재시도합니다(최대 8회, 간격 `400ms × attempts`).
|
|
408
|
+
- **프로젝션 등록 이중 트랙**: 우선 `ctx.root.get('sessionProjections')`(호스트 루트에 가장 가까운 것)를 사용하고, 얻지 못하면 주입 인스턴스로 폴백합니다. 주입 인스턴스에만 등록하면 클라이언트가 프로젝션 유닛을 읽지 못할 수 있어 알림 본문이 폴백 경로를 타게 됩니다 —— best-effort이며 세션 내 시스템 메시지에는 영향을 주지 않습니다.
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## 프로젝트 구조
|
|
413
|
+
|
|
414
|
+
```text
|
|
415
|
+
dsh-session-notify/
|
|
416
|
+
├── lib/
|
|
417
|
+
│ ├── index.js # 宿主平面(Node):session/event 订阅 → 系统消息落盘;
|
|
418
|
+
│ │ # settings 命名空间注册(schemastery schema,退避重试);
|
|
419
|
+
│ │ # sessionProjections 投影单元(后台会话推送正文)
|
|
420
|
+
│ ├── core.js # 纯逻辑层(零依赖,可独立测试):轮次计时与用量聚合、
|
|
421
|
+
│ │ # 5 语言文案表、时长/用量/缓存/速度格式化、
|
|
422
|
+
│ │ # 模板渲染({title}{duration}{usage}{error}{cache}{tps})
|
|
423
|
+
│ └── client.js # 浏览器平面:完成推送(系统通知 + toast)、
|
|
424
|
+
│ # 设置卡片(Chip 模板编辑器 + 预设系统 + 实时预览)
|
|
425
|
+
├── scripts/
|
|
426
|
+
│ ├── build.sh # 零构建:仅 node --check 语法校验
|
|
427
|
+
│ ├── verify-notice.mjs # 校验会话日志落盘证据(zstd 多帧逐帧解压)
|
|
428
|
+
│ ├── probe-client.mjs # 探针:客户端装配
|
|
429
|
+
│ ├── probe-client-e2e.mjs # 探针:客户端端到端
|
|
430
|
+
│ ├── probe-card-render.mjs # 探针:设置卡片渲染
|
|
431
|
+
│ ├── probe-settings-card.mjs # 探针:设置面板卡片
|
|
432
|
+
│ ├── probe-settings-check.mjs# 探针:设置面板检查
|
|
433
|
+
│ └── probe-diag-settings.mjs # 探针:settings 诊断
|
|
434
|
+
├── cordis.patch.yml # dsh.bundle manifest —— dsh plugin add 自动挂载的凭证
|
|
435
|
+
├── package.json # dsh.bundle(patch)+ dsh.client(web 注入)双 manifest;
|
|
436
|
+
│ # exports: "." / "./client" / "./core"
|
|
437
|
+
├── LICENSE # MIT
|
|
438
|
+
└── README.md # 本文档
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## 개발 및 디버깅
|
|
444
|
+
|
|
445
|
+
문법 검증(제로 빌드, `prepublishOnly`와 동일한 검사):
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
npm run build
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
배포(배포 전에 `prepublishOnly` 문법 검증 자동 실행):
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
npm publish --registry=https://registry.npmjs.org --access public
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
오프라인 검증: 세션 로그에서 모든 plugin-source 이벤트와 `turn/end` 꼬리 시퀀스를 추출합니다(경로를 넘기지 않으면 `~/.dsh/sessions`에서 가장 최근 세션 자동 선택):
|
|
458
|
+
|
|
459
|
+
```bash
|
|
460
|
+
node scripts/verify-notice.mjs <session.jsonl.zstd>
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### 디버깅 진입점
|
|
464
|
+
|
|
465
|
+
| 진입점 | 내용 |
|
|
466
|
+
| --- | --- |
|
|
467
|
+
| `~/.dsh/session-complete-notify.log` | 호스트 진단 로그: 설정 등록, 재시도 및 실패, 프로젝션 등록, 추가 실패 스택 |
|
|
468
|
+
| 브라우저 console `[dsh-session-notify-client]` | 클라이언트 로그: 권한 상태, 알림 표시, 설정 저장 |
|
|
469
|
+
| `window.__dsch_notify_debug.readNotice(id)` | 지정 세션의 최신 알림 본문 수동 읽기 |
|
|
470
|
+
| `window.__dsch_notify_debug.snapshotDebug(id)` | 세션 꼬리 노드 유형 + notice 개수 + 최근 본문(앞 200자) |
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
## 자주 묻는 질문
|
|
475
|
+
|
|
476
|
+
<details>
|
|
477
|
+
<summary><b>npm install 후에 왜 자동으로 마운트되지 않나요?</b></summary>
|
|
478
|
+
|
|
479
|
+
이는 DSH의 공식 설계입니다: `npm install`은 패키지를 의존성 트리에 넣을 뿐 플러그인을 등록하지 않습니다. 자동 마운트의 유일한 경로는 `dsh plugin add` —— 패키지 내 `dsh.bundle` manifest(이 플러그인은 0.1.3부터 선언)를 읽고 `cordis.patch.yml`을 자동 적용합니다. [설치](#설치)를 참고하세요.
|
|
480
|
+
|
|
481
|
+
</details>
|
|
482
|
+
|
|
483
|
+
<details>
|
|
484
|
+
<summary><b>왜 「중단」(interrupted)은 알림을 보내지 않나요?</b></summary>
|
|
485
|
+
|
|
486
|
+
`interrupted`는 크래시 복구 후 영속화 백엔드가 보완한 고아 턴 종료 표시이며, 사용자 관점의 「완료」에는 포함되지 않습니다(그렇지 않으면 세션 복구 시 오보로 화면이 넘칩니다). 꼭 필요하면 호스트 설정의 `reasons`에 추가할 수 있습니다.
|
|
487
|
+
|
|
488
|
+
</details>
|
|
489
|
+
|
|
490
|
+
<details>
|
|
491
|
+
<summary><b>백그라운드 세션(창을 열지 않은)도 알림이 오나요?</b></summary>
|
|
492
|
+
|
|
493
|
+
네. 클라이언트는 세션 목록 스냅샷에서 모든 세션의 `running` 엣지를 관찰합니다. 본문은 우선 호스트 프로젝션을 가져옵니다 —— 호스트가 모든 세션(백그라운드 포함)에 대해 프로젝션 유닛을 유지하므로 알림 본문이 세션 간에 일관됩니다. 프로젝션을 사용할 수 없으면 이벤트 윈도우 또는 작업 영역 정보로 폴백합니다.
|
|
494
|
+
|
|
495
|
+
</details>
|
|
496
|
+
|
|
497
|
+
<details>
|
|
498
|
+
<summary><b>설정 저장 후 왜 페이지 새로고침을 안내하나요?</b></summary>
|
|
499
|
+
|
|
500
|
+
호스트는 네임스페이스 등록 시 설정을 한 번 읽고, 클라이언트 bundle은 페이지 로드 시 어셈블됩니다. 저장 후 「클릭하여 새로고침」을 누르면 양쪽이 다시 읽어 새 언어와 템플릿이 적용됩니다.
|
|
501
|
+
|
|
502
|
+
</details>
|
|
503
|
+
|
|
504
|
+
<details>
|
|
505
|
+
<summary><b>캐시 적중률, 속도 데이터는 어디서 오나요? 왜 때로는 비어 있나요?</b></summary>
|
|
506
|
+
|
|
507
|
+
공식 `sessionProjections`(`tokenUsage`, `sessionStats`)에서 가져오며 dsh-web-ui 상태 표시줄과 동일한 기준입니다. 호스트가 프로젝션 스냅샷을 읽지 못하거나 데이터가 아직 준비되지 않으면 로컬 사용량 집계 추정으로 폴백하고, 그래도 데이터가 없으면 해당 항목은 비어 있습니다(라벨을 삽입해도 표시되지 않음). 또한 이 두 항목은 커스텀 템플릿에서 `{cache}`, `{tps}`로 삽입할 때만 나타나며 기본 문구에는 포함되지 않습니다.
|
|
508
|
+
|
|
509
|
+
</details>
|
|
510
|
+
|
|
511
|
+
<details>
|
|
512
|
+
<summary><b>알림 본문의 오류 정보가 너무 길고 줄바꿈이 있으면 어떻게 하나요?</b></summary>
|
|
513
|
+
|
|
514
|
+
요약 행(접히는 행)과 오류 상세 모두 한 줄로 정리되어 잘립니다: 요약 120자, 템플릿 `{error}` 80자, 기본 문구의 오류 상세 40자, 초과 시 말줄임표로 끝납니다.
|
|
515
|
+
|
|
516
|
+
</details>
|
|
517
|
+
|
|
518
|
+
<details>
|
|
519
|
+
<summary><b>시스템 알림의 아이콘이나 소리를 커스터마이징할 수 있나요?</b></summary>
|
|
520
|
+
|
|
521
|
+
현재 버전은 브라우저 기본 알림 스타일을 사용하며 커스텀 아이콘이나 소리를 주입하지 않습니다. toast는 고정된 다크 카드입니다. 이런 기능이 필요하면 Issue 또는 PR을 환영합니다.
|
|
522
|
+
|
|
523
|
+
</details>
|
|
524
|
+
|
|
525
|
+
<details>
|
|
526
|
+
<summary><b>왜 Edge는 시스템 알림을 보내지 못하나요? QQ 브라우저는 왜 페이지 내 배너(내장 푸시 팝업)만 있나요?</b></summary>
|
|
527
|
+
|
|
528
|
+
둘 다 브라우저 동작이며 플러그인이 강제할 수 없습니다:
|
|
529
|
+
|
|
530
|
+
- **Edge / Chrome**: "익숙하지 않은" 사이트에 대해 **알림을 자동 차단**합니다(주소창에 「알림 차단됨」 표시). 주소창 왼쪽 권한 아이콘 클릭 → 사이트 설정 → 알림 → 허용하면 복구되며, 이후 Windows 알림 센터가 정상적으로 표시됩니다. 브라우저 알림 설정에서 「자동 차단」을 끌 수도 있습니다.
|
|
531
|
+
- **QQ 브라우저 등 중국산 Chromium 셸**: `Notification`을 고정적으로 **브라우저 내장의 페이지 내 푸시 팝업**(페이지 상단/모서리 배너, Windows 알림 센터 미경유)으로 렌더링하며 시스템 알림 옵션이 없습니다. 세 가지 알림 방식의 실제 동작:
|
|
532
|
+
- `이중 채널` → 브라우저 내장 팝업 + 플러그인 toast, 페이지 내 알림 2개;
|
|
533
|
+
- `시스템 알림만` → 무효(QQ 브라우저는 항상 페이지 내 팝업으로 렌더링);
|
|
534
|
+
- `페이지 내 알림만` → 브라우저 내장 팝업이 나타나지 않고 페이지 내에는 플러그인 고유의 작은 toast만 표시(권장).
|
|
535
|
+
설정 패널의 각 사유별 「전송」 테스트 버튼도 이 규칙에 따라 렌더링됩니다.
|
|
536
|
+
- **Firefox**: 창 포커스 시 알림이 페이지 내 배너로 표시되고, 포커스 해제/최소화 시에만 시스템 알림 센터로 들어갑니다. 권한은 주소창에서 수동으로 허용해야 합니다.
|
|
537
|
+
- 참고: `http://IP` 접근(비보안 컨텍스트) 시 `Notification`이 존재하지 않아 어떤 브라우저에서도 시스템 알림을 띄울 수 없습니다.
|
|
538
|
+
|
|
539
|
+
설정 패널 「알림 권한」 영역에서 현재 상태와 해당 조작 안내를 실시간으로 표시합니다.
|
|
540
|
+
|
|
541
|
+
</details>
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
## 변경 로그
|
|
546
|
+
|
|
547
|
+
| 버전 | 날짜 | 변경 |
|
|
548
|
+
| --- | --- | --- |
|
|
549
|
+
| **0.1.10** | 2026-08-29 | 「알림 제목」을 네이티브 입력 상자로 변경(네이티브 placeholder 안내: 복사 불가, 입력 시 사라짐, 비우면 복원. 「+ 세션 제목」으로 커서 위치에 `{title}` 삽입). 문서에 QQ 브라우저 내장 푸시 팝업 설명 추가(세 가지 알림 방식의 실제 동작 + 전송 버튼 테스트도 동일 규칙) |
|
|
550
|
+
| **0.1.9** | 2026-08-29 | 알림 제목에 **사유별 커스터마이징** 지원(접는 영역 UI, 기본 접힘으로 비대하지 않음. 비워 두면 각 사유에 차별화된 기본 제목 사용: 작업 완료/작업 오류/작업 중단/작업 차단/작업 출력 상한 도달, 5개 언어). 프로젝션을 객체(kind/text/title)로 업그레이드해 host가 렌더링한 제목을 전달. 초기화 버튼이 **현재 언어를 유지**. 기본 문구에 「세션 제목」 라벨 내장(세션 「{title}」 완료, 제목 없으면 자동 폴백). 설정 패널 템플릿 미리보기 동기화. 「+ 정보 삽입」 후 라벨을 삽입해도 더 이상 자동으로 접히지 않음. 사용 중인 커스텀 프리셋을 삭제하면 기본으로 자동 전환. 템플릿 미리보기 수정(클릭해도 사라지지 않고, 입력 시에만 숨겨지며, 비우면 복원). 각 사유마다 「전송」 버튼 추가(원클릭으로 현재 템플릿이 렌더링한 테스트 알림 전송) |
|
|
551
|
+
| **0.1.8** | 2026-08-29 | 기본 알림 제목을 「작업 완료」로 변경(`{title}`은 여전히 세션 제목을 참조 가능). 기본 문구를 종료 사유별로 차별화 표현(완료는 압축된 괄호식 / 중단·차단은 문장 분리 / 오류는 오류 상세 선행 / 상한은 제안 첨부, 5개 언어). 설정 패널에 「초기화」 버튼 추가, 원클릭으로 기본값 복원 |
|
|
552
|
+
| **0.1.7** | 2026-08-29 | 0.1.6의 설정 카드 크래시 수정: `notificationPermissionRow`/`requestPermissionNow`가 Card 컴포넌트 내부 state(범위 밖)를 참조해 렌더링 ReferenceError가 발생하고 설정 카드 전체가 사라지던 문제를 수정. 자체 포함 + 콜백 전달 방식으로 변경 |
|
|
553
|
+
| **0.1.6** | 2026-08-29 | 설정 패널에 「알림 권한」 상태 영역 추가(권한 상태 실시간 표시 + 원클릭 권한 요청 버튼 + 차단 시 주소창 조작 안내). 권한 요청을 **사용자 제스처 내에서 요청**으로 변경(Chromium이 제스처가 아닌 자동 요청을 무시하므로, Edge가 익숙하지 않은 사이트의 알림을 자동 차단하는 전형적인 상황 해결). FAQ에 브라우저 차이 설명 추가 |
|
|
554
|
+
| **0.1.5** | 2026-08-29 | 「알림 방식」 설정 추가(이중 채널 / 시스템 알림만 / 페이지 내 알림만): QQ 브라우저 등 Chromium 셸이 `Notification`을 페이지 내 배너로 렌더링해 발생하는 이중 알림 문제 해결. `pushMode`를 설정 schema와 설정 패널에 추가 |
|
|
555
|
+
| **0.1.4** | 2026-08-28 | 완전한 제거 지원 추가: `dispose` 생명주기 마무리(host는 언로드 플래그를 설정해 대기 중인 마이크로태스크 추가를 억제. client는 본문 폴링 타이머, `__dsch_notify_debug` 훅, toast 컨테이너 정리). 제거 문서와 FAQ 동기화 |
|
|
556
|
+
| **0.1.3** | 2026-08-28 | 공식 `dsh.bundle` manifest 선언(`dsh plugin add` 한 줄 명령으로 자동 마운트). settings 재시도 타이머를 `ctx.effect()` 래핑으로 변경(Cordis effect 규율). 설치 문서 재정렬 |
|
|
557
|
+
| 0.1.2 | 2026-08-27 | 패키지를 `@telosmaylx` scope로 이름 변경(npm 사용자 이름 스코프) |
|
|
558
|
+
| 0.1.1 | 2026-08-27 | GitHub, npm 설치 방법 문서화 |
|
|
559
|
+
| 0.1.0 | 2026-08-26 | 초기 버전: 세션 내 시스템 메시지 + 브라우저 알림 + 공식 설정 패널 |
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
## 기여
|
|
564
|
+
|
|
565
|
+
Issue와 PR을 환영합니다:
|
|
566
|
+
|
|
567
|
+
1. 저장소를 Fork하고 새 브랜치를 만듭니다(`feat/xxx`)
|
|
568
|
+
2. 변경 후 `npm run build`를 실행해 문법을 검증합니다
|
|
569
|
+
3. PR을 제출하고 동기와 검증 방법을 설명합니다
|
|
570
|
+
|
|
571
|
+
제출 전에 [Cordis 개발 튜토리얼](https://deepseek-harness.github.io/deepseek-harness/develop/cordis-tutorial) 규율을 준수하세요:
|
|
572
|
+
|
|
573
|
+
- Cordis 외부 리소스(타이머, 구독, watcher)는 반드시 `ctx.effect()`에 감싸 disposer를 반환해야 합니다.
|
|
574
|
+
- 설정 항목에 명시적 `id`를 사용해 편집 드리프트를 방지합니다.
|
|
575
|
+
- 플러그인은 `dsh.bundle` manifest를 선언해야 `dsh plugin add`로 인식되어 설치됩니다.
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
## 관련 링크
|
|
580
|
+
|
|
581
|
+
- [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) —— DSH 플러그인 선별 목록(제출 규칙: `dsh.bundle`이 유일한 설치 증명)
|
|
582
|
+
- [Cordis 개발 튜토리얼](https://deepseek-harness.github.io/deepseek-harness/develop/cordis-tutorial) —— 플러그인 개발 전체 프로세스(01-07장)
|
|
583
|
+
- [npm 패키지 홈페이지](https://www.npmjs.com/package/@telosmaylx/dsh-session-notify)
|
|
584
|
+
- [GitHub 저장소](https://github.com/TelosmaYLX/dsh-session-notify)
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
## 라이선스
|
|
589
|
+
|
|
590
|
+
[MIT](./LICENSE) © dsh-session-notify contributors
|