@telosmaylx/dsh-session-notify 0.1.13 → 0.1.17

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 CHANGED
@@ -13,7 +13,7 @@
13
13
  [![DSH](https://img.shields.io/badge/DSH-Web%20Profile-4D6BFE)](https://www.npmjs.com/package/@telosmaylx/dsh-session-notify)
14
14
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/TelosmaYLX/dsh-session-notify/pulls)
15
15
 
16
- At the end of every conversation turn, writes "completed / errored / blocked / hit the cap" along with duration and token usage into the session log, and pushes a browser system notification and an in-page toast. Built-in 5 languages, 4 style presets (kaomoji / Felyne / Neko girl / DeepSeek-chan), a visual copy-template editor, and a custom preset library; cache hit rate and generation speed come from the official projections, consistent with the status bar.
16
+ At the end of every conversation turn, writes "completed / errored / blocked / hit the cap" along with duration and token usage into the session log, and pushes a browser system notification and an in-page toast; **when the AI asks you a question, it also alerts you immediately** — no need to watch the session page. Built-in 5 languages, 4 style presets (kaomoji / Felyne / Neko girl / DeepSeek-chan), a visual copy-template editor, and a custom preset library; cache hit rate and generation speed come from the official projections, consistent with the status bar.
17
17
 
18
18
  </div>
19
19
 
@@ -49,6 +49,16 @@ At the end of every conversation turn, writes "completed / errored / blocked / h
49
49
 
50
50
  ## Features
51
51
 
52
+ <div align="center">
53
+
54
+ <img src="screenshot/screenshot1.png" width="220" alt="Title editor">
55
+ <img src="screenshot/ScreenShot2png.png" width="220" alt="Content editor">
56
+ <img src="screenshot/ScreenShot3.png" width="220" alt="Completion notification">
57
+ <img src="screenshot/ScreenShot4.png" width="220" alt="AI question notification">
58
+ <img src="screenshot/ScreenShot5.png" width="220" alt="Error notification">
59
+
60
+ </div>
61
+
52
62
  ### Three-Channel Notifications, None Missed
53
63
 
54
64
  | Channel | Form | Description |
@@ -62,12 +72,17 @@ At the end of every conversation turn, writes "completed / errored / blocked / h
62
72
  - The host maintains a "most recent notification body" session projection unit (key = `session-complete-notify`) for all sessions, including background ones whose windows aren't open, so the pushed body is consistent across sessions and does not depend on you happening to have that window open.
63
73
  - The client observes the `running` flag of all sessions from the session list snapshot; a `true → false` edge triggers a push, following the same strategy as the official sidebar reminders (the first observation only records a baseline; sessions already idle are not back-filled).
64
74
 
75
+ ### Instant Question Alerts
76
+
77
+ - When the AI calls `ask_user_question` to ask you something, the host immediately writes the "question title + body" into a dedicated projection unit (key = `session-complete-notify-question`), and the client polls it in real time and pops an alert — **even if you are looking at another page, you won't miss the question**.
78
+ - The question copy is fully customizable: the title follows the "per-reason title → global title → default title" chain, the body supports the `{question}` placeholder (injected with the AI's actual question), and the `{image}` / `{icon}` media switches work as well.
79
+
65
80
  ### Customizable Down to Every Sentence
66
81
 
67
82
  - **5 languages**: Simplified Chinese, Traditional Chinese, English, Japanese, Korean — the notification copy, the duration and usage wording, and the settings panel UI all switch with the language (instant re-render on switch).
68
83
  - **Visual template editor** (Chip editor): dynamic information renders as inline chips (placeholder code never leaks); "+ Insert Info" inserts at the cursor (can be inserted mid-text); clicking a chip removes it; each field has a live preview (information flows into the body with sample values).
69
- - **Preset system**: a built-in "Default" baseline plus 4 one-click style presets (kaomoji / Felyne / Neko girl / DeepSeek-chan — a full set of stylized titles and per-reason copy); the current configuration can be saved as a custom preset (persisted in `localStorage`), supports auto-numbered unnamed presets (`Untitled`, `Untitled 2`…), a "From: xxx · Modified" origin indicator, and deleting presets.
70
- - **Push title template**: when left empty, each reason uses a default title (completed = task completed / errored = task errored / …); `{title}` references the session title.
84
+ - **Preset system**: a built-in "Default" baseline plus 4 one-click style presets (kaomoji / Felyne / Neko girl / DeepSeek-chan — a full set of stylized copy for the title, all 5 end reasons and the question); the current configuration can be saved as a custom preset (persisted in `localStorage`), supports auto-numbered unnamed presets (`Untitled`, `Untitled 2`…), a "From: xxx · Modified" origin indicator, and deleting presets.
85
+ - **Push title template**: when left empty, each reason uses a default title (completed = task completed / errored = task errored / … / question = AI is asking you a question); `{title}` references the session title.
71
86
 
72
87
  ### Same Source as the Official Metrics
73
88
 
@@ -222,6 +237,8 @@ When each conversation turn ends (`turn/end`), the end reason is checked; hittin
222
237
 
223
238
  **Subagent sessions are skipped by default** (`header.origin === 'subagent'` or `delegationDepth > 0`) — subagents are orchestrated by their parent session, so per-turn notifications are noise; the skip can be disabled in the host configuration.
224
239
 
240
+ **Question alerts are a separate channel and do NOT go through the whitelist above**: when the AI calls `ask_user_question` and waits for your answer (a `tool/call` event), the alert fires immediately, and it becomes invalid once the `tool/result` returns. Questions are not written to the session log — they only pop a notification.
241
+
225
242
  ### Where Does the Push Body Come From
226
243
 
227
244
  When the client observes a `running: true → false` edge in the session list, it pushes; the body is fetched with the following priority (polls for up to 6 seconds at 400ms intervals):
@@ -230,20 +247,22 @@ When the client observes a `running: true → false` edge in the session list, i
230
247
  2. **The notice node in the session event window** (`kind=context` + `form=notice`) — for the session being viewed, available immediately after persistence;
231
248
  3. **Fallback** — "See the in-session system message for details" + workspace info (last segment of `cwd`).
232
249
 
250
+ Question alerts take their body from the host projection as well (key = `session-complete-notify-question`, with the title and body already rendered by the host); on older hosts without that projection, the client assembles the title and `{question}` text itself as a fallback.
251
+
233
252
  ### Notification Examples
234
253
 
235
- All of the following are actually generated by `buildNotice` in `lib/core.js`. The default copy is uniform — "Session \"{title}\" <state>. Click to view." (wording varies per end reason; **duration and usage are not included**):
236
-
237
- English default copy (session title in double quotes):
238
-
239
- ```text
240
- Session "重构登录模块" completed. Click to view. ← completed
241
- Session "重构登录模块" aborted. Click to view. ← aborted
242
- Session "重构登录模块" blocked. Click to view. ← blocked
243
- Session "重构登录模块" hit the cap. Click to view. ← hit the cap
244
- Session "重构登录模块" failed. Click to view. ← errored
245
- ```
246
-
254
+ All of the following are actually generated by `buildNotice` in `lib/core.js`. The default copy is uniform — "Session \"{title}\" <state>. Click to view." (wording varies per end reason; **duration and usage are not included**):
255
+
256
+ English default copy (session title in double quotes):
257
+
258
+ ```text
259
+ Session "重构登录模块" completed. Click to view. ← completed
260
+ Session "重构登录模块" aborted. Click to view. ← aborted
261
+ Session "重构登录模块" blocked. Click to view. ← blocked
262
+ Session "重构登录模块" hit the cap. Click to view. ← hit the cap
263
+ Session "重构登录模块" failed. Click to view. ← errored
264
+ ```
265
+
247
266
  > When the session has no title (`titleValue` empty), it falls back to "Session completed. Click to view."; duration / usage / cache hit / speed only appear in custom templates via the `{duration}` `{usage}` `{cache}` `{tps}` placeholders.
248
267
 
249
268
  Custom template (edited in the settings panel; this example uses all info slots):
@@ -281,7 +300,7 @@ Session "重构登录模块" completed (took 3m25s, used 1,240 in / 3,560 out).
281
300
 
282
301
  ## Configuration
283
302
 
284
- Most configuration is done in the **DSH Web UI → Settings → Plugins → Session Completion Notify** panel (takes effect after saving and clicking "Click to Refresh"). Only "Trigger reason whitelist" and "Skip subagents" are configured in the `config` of the host `cordis.patch.yml`.
303
+ Most configuration is done in the **DSH Web UI → Settings → Plugins → Session Completion Notify** panel (takes effect after saving and clicking "Click to Refresh"). Only the "Trigger reason whitelist" is configured in the `config` of the host `cordis.patch.yml` (skipping subagents is controlled by a checkbox in the panel).
285
304
 
286
305
  ### Settings Panel
287
306
 
@@ -293,12 +312,12 @@ The panel is registered in the official "Settings → Plugins" panel (`settings.
293
312
  | Language | Radio selection among 5 languages; switching instantly re-renders the whole panel |
294
313
  | Push mode | Choose one of three: dual channel (system notification + in-page toast, default) / system notification only / in-page only |
295
314
  | Notification media | Two sources for the large image: **per-reason upload** — insert the `{image}` token via "+ Insert info → Image" in a template and pick a local file (shown as a chip with a thumbnail in the editor, auto-compressed to 512px wide with a 16:9 center crop matching the notification display ratio, saved per reason); **global image/icon** — two upload cards side by side in one row (**icon first**; empty = a rounded "+" tile; click to upload; **image 512×288 with a 16:9 center crop, icon 128×128 with a 1:1 square center crop**; once uploaded the card shows the thumbnail — **click it for a fullscreen preview of the full original image (aspect-ratio-preserving, uncropped)**, the "×" at the top-right removes it). Icon left empty = site default icon, or insert the `{icon}` token in a template for a **per-reason icon** (takes precedence over the global one). Effective on the system-notification channel only (the in-page toast is a text card); the "Send" test buttons apply them too |
296
- | Title | A collapsible section (**collapsed by default**, click to expand): the **global push title** (shared by all reasons; a Chip editor — info inserted via "+ Insert info" shows as **chip tags**, click a chip to remove; **when the notification is sent, info tokens in the title (duration / usage / error / cache / speed) are replaced with the real values, never shown as raw codes**; when left empty, each reason uses a default title — completed = task completed, errored = task errored, aborted = task aborted, blocked = task blocked, cap = task hit the output cap) plus **per-reason titles** (5 inputs, one per reason; empty = use the global or the language default) |
297
- | Content | A collapsible section (**collapsed by default**, click to expand). When expanded, each end reason (completed, errored, aborted, blocked, output cap) is a **single-row layout** (reason label + Chip editor + a "+" insert button that turns into "−" while the menu is open + a **paper-plane send button**; the buttons are rectangular and vertically centered): **with an empty template (default preset) the editor shows the default text**; text + inline info chips, insert at the cursor; for `{image}`/`{icon}` chips **click the thumbnail to preview the full image, only the "×" removes it** (prevents accidental removal); other chips are removed by clicking; **after editing, clearing shows the "leave empty to use the default text" placeholder (non-selectable/non-deletable)** |
315
+ | Title | A collapsible section (**collapsed by default**, click to expand): the **global push title** (shared by all reasons; a Chip editor — info inserted via "+ Insert info" shows as **chip tags**, click a chip to remove; **when the notification is sent, info tokens in the title (duration / usage / error / cache / speed) are replaced with the real values, never shown as raw codes**; when left empty, each reason uses a default title — completed = task completed, errored = task errored, aborted = task aborted, blocked = task blocked, cap = task hit the output cap, question = AI is asking you a question) plus **per-reason titles** (6 inputs, one per reason, each with a "+" insert button for info tokens — including "Question", no images/icons; inserted at the caret; **takes precedence over the global title**; empty = use the global or the language default) |
316
+ | Content | A collapsible section (**collapsed by default**, click to expand). When expanded, each reason (completed, errored, aborted, blocked, output cap, question) is a **single-row layout** (reason label + Chip editor + a "+" insert button that turns into "−" while the menu is open + a **paper-plane send button**; the buttons are rectangular and vertically centered): **with an empty template (default preset) the editor shows the default text**; text + inline info chips, insert at the cursor; for `{image}`/`{icon}` chips **click the thumbnail to preview the full image, only the "×" removes it** (prevents accidental removal); other chips are removed by clicking; **after editing, clearing shows the "leave empty to use the default text" placeholder (non-selectable/non-deletable)**; the question row's default text is "AI is asking: {question}", where `{question}` is replaced by the AI's actual question when sent (the insert menu offers a "Question" token too, with the same interaction as the other tokens) |
298
317
  | Skip subagent sessions | Checkbox (written into the settings document on save) |
299
318
  | Notification permissions | Status shown in real time: granted (green) / not yet granted (with a "Request Authorization" button) / blocked by the browser (with address-bar instructions) / environment unsupported |
300
319
  | Per-reason titles | Collapsible area (collapsed by default): one title input per end reason; empty = use the global template or the language default title |
301
- | Save | Writes to the host settings document (`language` / `templates` / `titleTemplate` / `titleTemplates` / `pushMode`); shows a "Click to Refresh" link after saving |
320
+ | Save | Writes to the host settings document (`language` / `templates` / `titleTemplate` / `titleTemplates` / `pushMode` / `skipSubagents`); shows a "Click to Refresh" link after saving |
302
321
  | Reset | One-click restore of default values (**the current language is kept**, titles/templates/push mode restore to defaults) and saves immediately |
303
322
 
304
323
  > [!NOTE]
@@ -324,6 +343,7 @@ Each end reason has its own template input; **the token is the switch** — the
324
343
  | `{tps}` | Generation speed (per the official projection; empty when no data) | `92 tok/s` |
325
344
  | `{image}` | Custom notification-image switch: insert via "+ Insert info → Image" and pick a local file (auto-compressed to 512px), independent per reason; stripped from the rendered body, never written into the session log; removing the token also clears that reason's image data | — |
326
345
  | `{icon}` | Custom notification-icon switch: insert via "+ Insert info → Icon" and pick a local file (auto-compressed to 128×128 square), independent per reason; stripped from the rendered body, never written into the session log; takes precedence over the global "Notification icon"; removing the token also clears that reason's icon data | — |
346
+ | `{question}` | **Question-row-only placeholder**: replaced with the AI's actual question text when sent; integrated with the "+ Insert info" menu (pick the "Question" token, or just type it — both render as a chip); only available in the question channel, inserted in other reason rows it is replaced with an empty string (no literal leak) | `Should I continue generating the report?` |
327
347
  | `{label}` | Deprecated — automatically stripped at render time; old templates remain compatible (the option has been removed from the insert menu) | — |
328
348
 
329
349
  An empty template uses the built-in default copy (the uniform "Session "{title}" <state>. Click to view." phrasing; duration and usage are not included). The collapsible row's `summary` shares the same source as the body (the rendered result is truncated to 120 characters) — users who only look at the collapsible row still see the real title, duration and usage.
@@ -365,6 +385,7 @@ The plugin is split into a **host plane** (Node) and a **client plane** (browser
365
385
  │ session/event 火线 │
366
386
  │ ├─ turn/start → tracker 起表(key: sessionId:turn) │
367
387
  │ ├─ assistant/message → 累加该轮 token 用量 │
388
+ │ ├─ tool/call → ask_user_question?写提问投影(标题+正文) │
368
389
  │ └─ turn/end → reason.kind ∈ reasons ? │
369
390
  │ ├─ 子代理会话?跳过 │
370
391
  │ ├─ 读官方投影:cache / tps / title │
@@ -374,6 +395,8 @@ The plugin is split into a **host plane** (Node) and a **client plane** (browser
374
395
  │ │
375
396
  │ settings.register → 官方「设置 → 插件」命名空间(失败退避重试) │
376
397
  │ sessionProjections → 注册投影单元(key=session-complete-notify) │
398
+ │ + 提问投影(key=session-complete-notify- │
399
+ │ question,等待回答期间持续推送) │
377
400
  └──────────────────────────────┬──────────────────────────────────────┘
378
401
  │ user/message (source: plugin, form: notice)
379
402
  ▼ JSONL 持久化 + 投影推送
@@ -383,6 +406,8 @@ The plugin is split into a **host plane** (Node) and a **client plane** (browser
383
406
  │ ├─ 取正文:投影 → 事件窗口 notice → 降级(轮询 ≤6s) │
384
407
  │ ├─ Web Notification(独立 tag,点击聚焦) │
385
408
  │ └─ 页内 toast(永远展示,≤3 条,10s 自动消失) │
409
+ │ 提问投影轮询(key=session-complete-notify-question): │
410
+ │ 有值 → 立即弹提醒(标题+正文),无值清空 │
386
411
  │ │
387
412
  │ slots.inject('settings.plugin.item') → 设置卡片(预设/语言/模板) │
388
413
  └─────────────────────────────────────────────────────────────────────┘
@@ -410,7 +435,8 @@ dsh-session-notify/
410
435
  │ │ # sessionProjections 投影单元(后台会话推送正文)
411
436
  │ ├── core.js # 纯逻辑层(零依赖,可独立测试):轮次计时与用量聚合、
412
437
  │ │ # 5 语言文案表、时长/用量/缓存/速度格式化、
413
- │ │ # 模板渲染({title}{duration}{usage}{error}{cache}{tps}
438
+ │ │ # 模板渲染({title}{duration}{usage}{error}{cache}{tps})、
439
+ │ │ # 提问正文构建(buildQuestionBody,{question} + 媒体剥除)
414
440
  │ └── client.js # 浏览器平面:完成推送(系统通知 + toast)、
415
441
  │ # 设置卡片(Chip 模板编辑器 + 预设系统 + 实时预览)
416
442
  ├── scripts/
@@ -471,6 +497,13 @@ This is DSH's official design: `npm install` only puts the package into the depe
471
497
 
472
498
  </details>
473
499
 
500
+ <details>
501
+ <summary><b>Will I also be alerted when the AI asks me a question?</b></summary>
502
+
503
+ Yes. When the AI calls `ask_user_question` and waits for your answer, the host immediately writes the "question title + body" into a dedicated projection (key = `session-complete-notify-question`), and the client pops an alert as soon as it polls the value — even if you are looking at another page, you won't miss it. The question copy is as customizable as the completion notifications: the "Title / Content" sections of the settings panel each have a "Question" row, the body supports the `{question}` placeholder (injected with the AI's actual question), and the `{image}` / `{icon}` media switches work too. Once you answer (`tool/result`), the alert is invalidated and does not linger.
504
+
505
+ </details>
506
+
474
507
  <details>
475
508
  <summary><b>Why is "interrupted" not notified?</b></summary>
476
509
 
@@ -533,27 +566,31 @@ The "Notification Permissions" area of the settings panel shows the current stat
533
566
 
534
567
  ---
535
568
 
536
- ## Changelog
537
-
538
- | Version | Date | Changes |
539
- | --- | --- | --- |
540
- | **0.1.13** | 2026-08-29 | Added **4 one-click style presets** (kaomoji / Felyne / Neko girl / DeepSeek-chan a full set of stylized copy for the title + all 5 reasons, **available in 5 languages**, loading never forces a UI-language switch); cleaned the obsolete old template names (playful/formal/detail) out of the preset dropdown; README default-copy examples synced to the "Session \"{title}\" <state>. Click to view." phrasing (duration and usage not included) |
541
- | **0.1.12** | 2026-08-29 | packaging cleanup: removed the temporary audit script that had been packed into scripts |
542
- | **0.1.11** | 2026-08-29 | New **custom notification media**: ① the template "+ Insert info" menu gains an **Image** token — insert `{image}` and pick a local file (**auto-compressed to 512px wide with a 16:9 center crop matching the notification display ratio**, shown as a thumbnail chip in the editor), uploaded per reason and stored in the settings document (stripped from the rendered body, never written into the session log); ② **notification image/icon are now upload cards** (empty = a rounded "+" tile, click to upload; **image 512×288 with a 16:9 center crop, icon 128×128 with a 1:1 square center crop**; once uploaded the card shows the thumbnail with a "×" at the top-right to remove it) — the crop guarantees the uploaded image appears complete in the notification card instead of being hard-cropped by the system's display area; ③ the **push-title "+ Insert info"** inserts any info token (session title / duration / usage / cache hit / speed), not just the title; the "Notification media" area in the settings panel now shows a **processing guide** (crop ratios) and a **thumbnail preview** after upload; ④ **per-reason notification icons** (insert the `{icon}` token in a template + local upload, 128×128 square, takes precedence over the global icon); ⑤ **removing an `{image}`/`{icon}` token clears that reason's image/icon data**; ⑥ **layout polish: "Body templates × 5" is now a collapsible section (collapsed by default to keep the panel compact, with a customized-count hint in the header, click to expand)**; ⑦ **layout polish ②: the notification image/icon upload cards sit side by side in one row; the push title and per-reason titles merge into a "Title" collapsible section (collapsed by default); collapse indicators now use triangle icons (the "Expand/Collapse" texts removed, reducing i18n burden)**; ⑧ **interaction polish: the push title is now a Chip editor (inserted info shows as chip tags instead of raw `{title}` codes); the image/icon cards are reordered (icon first); "Body templates × 5" renamed to "Content"; image/icon thumbnails open a fullscreen lightbox preview on click, and only the "×" removes a tag (no accidental removal)**; ⑨ **previews unified to the full original: upload now stores both the cropped version (for the notification) and an aspect-preserving full version (1024px, used by the lightbox), so card and chip previews both show the uncropped image; the collapse triangle icons are enlarged**; ⑩ **title info-token fix: `{duration}` `{usage}` `{error}` `{cache}` `{tps}` in the notification title are replaced with the session's real values, never leaking raw codes (chips in the editor, real data when sent); "Content" rows are now single-line (reason label + editor + a "+/−" insert button + a paper-plane send button)**; ⑪ **details: the live preview under each Content row is removed (more compact); the Content editors use a tighter padding (compact); the "+/−" and paper-plane buttons now have rectangular borders and are vertically centered**; ⑫ **deletion-experience fix: clearing a Content editor no longer restores the default text (it shows the placeholder hint instead), and the caret returns to the end after clearing — holding Backspace now deletes continuously; "+/−" now use SVG stroke icons (exactly centered in the button); the paper-plane icon is rotated 30° counterclockwise**; ⑬ **placeholder & default text: with an empty template (default preset) the editor directly shows the default text (what you see is what gets pushed); after editing, clearing shows the "leave empty to use the default text" placeholder — implemented with a CSS pseudo-element (behaves like a native input placeholder: non-selectable, non-deletable), replacing the previously copyable placeholder text**; ⑭ **details ②: the push-title placeholder now reads "Generic push title; leave empty to use the default title (per-reason titles below take precedence)"; clicking an empty editor moves the caret to the very start (typed text begins at the leftmost position, like a native placeholder); the paper-plane icon is rotated another 30° counterclockwise (60° total); the Content reason labels are now auto-width (editor hugs the label text); editor heights are unified (box-sizing border-box + min-height 38px, matching the plain inputs)**; ⑮ **details ③: the reason labels are back to a fixed 70px width (Content and per-reason titles aligned); Backspace/Delete on a chip is now handled manually so the caret stays at the deletion point (no more jumping to the start); the send button icon is now a bell (push/notification semantics); custom presets now also save the notification image/icon (including the full preview versions), the default preset stays empty**; ⑯ **details ④: the image/icon upload cards are now equal height (64px; the 16:9 image is 114px wide); the reason labels shrink from 70px to 60px; the send button icon changed from a bell to a rightward push arrow; the "Title" and "Content" collapsible headers are bold**; ⑰ **default text & labels: max-tokens is now uniformly shown as "Cap" (label / default title / placeholder, synced in all 5 languages); the per-reason default body is now "Session "{title}" completed. Click to view." (5 languages, editor hint and host rendering in sync); the per-reason title rows each gain a "+" insert button (like the push title: info tokens only, no image/icon, inserted at the caret)**; ⑱ **chips & preset management: the per-reason titles are now Chip editors (inserted info shows as chip tags instead of raw {title} codes; {image}{icon} are blocked — typed literally they stay as text and are stripped when sent); custom presets gain a "Rename" action (appears when a custom preset is selected; the dropdown and the active preset sync after renaming)**; ⑲ **details ⑤: Chip editor text is now vertically centered (line-height and chips unified to 20px, 9px vertical padding — single-line text sits centered in the 38px box); the "Title" and "Content" fold bars now have a filled background for contrast (rounded tinted bar as header, expanded body indented with a left border line); full audit of all 86 i18n keys across the 5 languages (all translated — tweaked zh-TW "Rename" and ko "Discard")**; the "Send" test buttons apply them too; effective on the system-notification channel only (the in-page toast is a text card); ⑳ **details ⑥: the filled background of the "Title" / "Content" fold headers is removed; the placeholder text color is now rgba(127,127,127,0.5) (lighter, no longer affected by the theme variable)**; the "Send" test buttons apply them too; effective on the system-notification channel only (the in-page toast is a text card); ㉑ **fix: the divider lines between the per-reason rows under Content are removed; when Content is expanded, the line under the header is now present and spans the full width, matching Title; Backspace/Delete on a chip no longer jumps the caret to the start (inside the element container the chip is located by child-node index, covering the caret-right-after-chip and editor-end cases); renaming an unnamed preset upgrades it to a named preset (new id) so the dropdown shows the new name immediately**; the "Send" test buttons apply them too; effective on the system-notification channel only (the in-page toast is a text card); ㉒ **fix: the placeholder hint no longer lingers after typing (the placeholder CSS selector is now aligned with the state attribute — it disappears as you type and text starts at the left); uploading/removing an image or icon keeps the caret in place instead of jumping to the start (the caret offset is saved before the DOM rebuild and restored after); the preset dropdown no longer shows a white focus/selected outline**; the "Send" test buttons apply them too; effective on the system-notification channel only (the in-page toast is a text card); ㉓ **fix: deleting a chip (including image/icon) no longer moves or hides the caret, so backspace-deleting is continuous — the rebuild triggered by image-data changes is now on-demand (skipped when the DOM no longer contains that media chip and the template has no such tag, keeping the caret at the deletion point; only uploads/replacements that need a thumbnail refresh trigger a rebuild), and the caret-offset math was fixed (a chip counts as exactly one position, never walking into its inner text)**; the "Send" test buttons apply them too; effective on the system-notification channel only (the in-page toast is a text card) |
543
- | **0.1.10** | 2026-08-29 | "Push title" changed to a native input (native placeholder behavior: not copyable, disappears when typing, restores when cleared; "+ Session Title" inserts `{title}` at the cursor); docs add the QQ Browser built-in push popup explanation (actual behavior of the three push modes + the send button tests follow the same rule) |
544
- | **0.1.9** | 2026-08-29 | Push titles now support **per-reason customization** (collapsible UI, collapsed by default to stay lean; when empty, each reason uses a differentiated default title: task completed / task errored / task aborted / task blocked / task hit the output cap, in 5 languages); the projection upgraded to an object (kind/text/title) carrying the host-rendered title; the reset button **keeps the current language**; the default copy embeds the "session title" token (Session "{title}" completed, auto-falls back when there's no title); settings-panel template previews sync; "+ Insert Info" no longer auto-collapses after inserting a token; deleting a custom preset currently in use automatically switches back to the default; template preview fixed (doesn't hide on click, hides only when typing, restores when cleared); each reason gains a "Send" button (one-click test notification rendered with the current template) |
545
- | **0.1.8** | 2026-08-29 | Default push title changed to "Task completed" (`{title}` can still reference the session title); default copy is differentiated per end reason (completed = compact parentheses / aborted·blocked = separate sentences / errored = error first / cap = with a suggestion, in 5 languages); the settings panel gains a "Reset" button for one-click restore of defaults |
546
- | **0.1.7** | 2026-08-29 | Fix the 0.1.6 settings-card crash: `notificationPermissionRow`/`requestPermissionNow` referenced Card-component-internal state (out of scope), causing a render ReferenceError and the whole settings card to disappear; changed to self-contained + callback params |
547
- | **0.1.6** | 2026-08-29 | The settings panel gains a "Notification permissions" status area (grant state in real time + one-click request-authorization button + address-bar guide when blocked); authorization changed to **requesting within a user gesture** (Chromium ignores non-gesture automatic requests; the typical Edge auto-block-for-unfamiliar-sites scenario is solved); FAQ adds browser-difference explanations |
548
- | **0.1.5** | 2026-08-29 | New "Push mode" setting (dual channel / system notification only / in-page only): solves the double-prompt caused by Chromium shells like QQ Browser rendering `Notification` as an in-page banner; `pushMode` added to the settings schema and settings panel |
549
- | **0.1.4** | 2026-08-28 | Complete uninstall support added: `dispose` lifecycle teardown (host sets an uninstalled flag to suppress pending microtask appends; client cleans up the body-polling timer, the `__dsch_notify_debug` hook, and the toast container); uninstall docs and FAQ updated accordingly |
550
- | **0.1.3** | 2026-08-28 | Declare the official `dsh.bundle` manifest (`dsh plugin add` auto-mounts with a single command); settings retry timer wrapped in `ctx.effect()` (Cordis effect discipline); installation docs rearranged |
551
- | 0.1.2 | 2026-08-27 | Package renamed to the `@telosmaylx` scope (npm username scope) |
552
- | 0.1.1 | 2026-08-27 | Documented the GitHub and npm installation methods |
553
- | 0.1.0 | 2026-08-26 | Initial version: in-session system messages + browser push + official settings panel |
554
-
555
- ---
556
-
569
+ ## Changelog
570
+
571
+ | Version | Date | Changes |
572
+ | --- | --- | --- |
573
+ | **0.1.17** | 2026-08-30 | **Instant question alerts (customizable)**: alerts immediately when the AI asks; question copy supports the `{question}` placeholder and media switches; all 4 presets gain question copy in 5 languages; fallback for older hosts |
574
+ | **0.1.16** | 2026-08-30 | **Interaction fix**: holding Backspace no longer deletes chips prematurely (chips are deleted only when no text sits between the caret and the chip) |
575
+ | **0.1.15** | 2026-08-30 | **Interaction polish**: Content fold is expanded by default; after deleting a chip the caret lands directly on real content for continuous deletion |
576
+ | **0.1.14** | 2026-08-30 | **Code-review fixes**: preset-delete confirmation, preset restores to saved config, media × clears the preview, per-reason images participate in preset matching, duplicate-name warning, reset enabled only when dirty, debug log auto-truncation |
577
+ | **0.1.13** | 2026-08-29 | Added 4 one-click style presets (kaomoji / Felyne / Neko girl / DeepSeek-chan), all in 5 languages |
578
+ | **0.1.12** | 2026-08-29 | Release cleanup |
579
+ | **0.1.11** | 2026-08-29 | **Custom notification media**: insert `{image}`/`{icon}` tokens and upload images/icons (auto-cropped); title supports info placeholders; "Body templates × 5" becomes a fold section; layout & interaction polish |
580
+ | **0.1.10** | 2026-08-29 | Native input for the push title; added multilingual READMEs (English / 繁體 / 日本語 / 한국어) |
581
+ | **0.1.9** | 2026-08-29 | Per-reason push titles; projection upgraded to an object; reset keeps the language; per-reason "Send" test buttons |
582
+ | **0.1.8** | 2026-08-29 | Default title "Task completed"; per-reason default copy; added a Reset button |
583
+ | **0.1.7** | 2026-08-29 | Fixed the settings-card crash (notification permission row scope issue) |
584
+ | **0.1.6** | 2026-08-29 | Notification permission status area; permission requested within a user gesture |
585
+ | **0.1.5** | 2026-08-29 | Push channel setting (dual / system-only / in-page-only) — fixes double alerts on QQ Browser |
586
+ | **0.1.4** | 2026-08-28 | Full uninstall support (dispose lifecycle cleanup) |
587
+ | **0.1.3** | 2026-08-28 | Declared dsh.bundle manifest; settings retry timer wrapped in ctx.effect() |
588
+ | 0.1.2 | 2026-08-27 | Renamed to `@telosmaylx` scope |
589
+ | 0.1.1 | 2026-08-27 | Documented GitHub & npm install methods |
590
+ | 0.1.0 | 2026-08-26 | Initial release: in-session system message + browser push + official settings panel |
591
+
592
+ ---
593
+
557
594
  ## Contributing
558
595
 
559
596
  Issues and PRs are welcome:
package/README.ja.md CHANGED
@@ -13,7 +13,7 @@
13
13
  [![DSH](https://img.shields.io/badge/DSH-Web%20Profile-4D6BFE)](https://www.npmjs.com/package/@telosmaylx/dsh-session-notify)
14
14
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/TelosmaYLX/dsh-session-notify/pulls)
15
15
 
16
- 各ターンの会話終了時に、「完了 / エラー / ブロック / 上限到達」を所要時間・トークン消費とともにセッションログへ書き込み、ブラウザのシステム通知とページ内トーストをプッシュします。5 言語、4 つのスタイルプリセット(顔文字 / アイルー / ネコ娘 / DeepSeekちゃん)、ビジュアルなメッセージテンプレートエディタ、カスタムプリセットライブラリを内蔵。キャッシュヒット率と生成速度は公式プロジェクションから取得し、ステータスバーと同じ口径です。
16
+ 各ターンの会話終了時に、「完了 / エラー / ブロック / 上限到達」を所要時間・トークン消費とともにセッションログへ書き込み、ブラウザのシステム通知とページ内トーストをプッシュします。**AI からの質問も即座にポップアップでお知らせ**します。5 言語、4 つのスタイルプリセット(顔文字 / アイルー / ネコ娘 / DeepSeekちゃん)、ビジュアルなメッセージテンプレートエディタ、カスタムプリセットライブラリを内蔵。キャッシュヒット率と生成速度は公式プロジェクションから取得し、ステータスバーと同じ口径です。
17
17
 
18
18
  </div>
19
19
 
@@ -49,6 +49,16 @@
49
49
 
50
50
  ## 機能概要
51
51
 
52
+ <div align="center">
53
+
54
+ <img src="screenshot/screenshot1.png" width="220" alt="タイトルエディタ">
55
+ <img src="screenshot/ScreenShot2png.png" width="220" alt="本文エディタ">
56
+ <img src="screenshot/ScreenShot3.png" width="220" alt="タスク完了通知">
57
+ <img src="screenshot/ScreenShot4.png" width="220" alt="AI 質問通知">
58
+ <img src="screenshot/ScreenShot5.png" width="220" alt="タスクエラー通知">
59
+
60
+ </div>
61
+
52
62
  ### 3 チャンネルで通知、取りこぼしなし
53
63
 
54
64
  | チャンネル | 形式 | 説明 |
@@ -62,12 +72,17 @@
62
72
  - ホストはすべてのセッション(バックグラウンド、未表示ウィンドウを含む)について「最新の通知本文」のセッションプロジェクションを維持します(key = `session-complete-notify`)。プッシュ本文はセッション間で一貫し、たまたまそのウィンドウを開いているかどうかに依存しません。
63
73
  - クライアントはセッションリストのスナップショットから全セッションの `running` ビットを監視し、`true → false` のエッジでプッシュをトリガーします。公式サイドバー通知と同じ戦略です(初回監視ではベースラインを記録するだけで、すでに idle のセッションには追って送信しません)。
64
74
 
75
+ ### 質問の即時通知
76
+
77
+ - AI が `ask_user_question` を呼び出して質問すると、ホストは即座に「質問タイトル + 本文」を専用プロジェクション(key = `session-complete-notify-question`)へ書き込み、クライアントがリアルタイムにポーリングしてポップアップ通知します——**別のページを見ていても質問を見逃しません**。
78
+ - 質問の文言は完全にカスタマイズ可能です。タイトルは「理由別タイトル → グローバルタイトル → デフォルトタイトル」の順に解決され、本文は `{question}` プレースホルダー(AI の実際の質問を注入)に対応し、`{image}` / `{icon}` のメディアスイッチも同様に有効です。
79
+
65
80
  ### 一言一句までカスタマイズ可能
66
81
 
67
82
  - **5 言語**:簡体中文、繁體中文、English、日本語、한국어 —— 通知メッセージ、所要時間・消費量の表現、設定パネルの UI がすべて言語に応じて切り替わります(切り替え時に即再レンダリング)。
68
83
  - **ビジュアルテンプレートエディタ**(Chip カプセルエディタ):動的情報をインラインカプセルとしてレンダリング(プレースホルダーコードは露出しません)。「+ 情報を挿入」でカーソル位置に挿入(テキストの途中にも挿入可)、カプセルをクリックで削除、各欄にリアルタイムプレビュー(情報がサンプル値として本文に流れ込みます)。
69
- - **プリセットシステム**:「デフォルト」ベースライン + ワンクリックのスタイルプリセット 4 種(顔文字 / アイルー / ネコ娘 / DeepSeekちゃん——タイトルと 5 理由の文面一式をスタイル化)。現在の設定をカスタムプリセットとして保存可能(`localStorage` に永続化)。自動採番される無名プリセット(`未命名`、`未命名 2`…)、「出自:xxx · 変更あり」の出所表示、プリセット削除に対応。
70
- - **プッシュタイトルテンプレート**:空欄の場合は各理由でデフォルトタイトルを使用(完了=タスク完了 / エラー=タスクエラー / …)。`{title}` はセッションタイトルを参照します。
84
+ - **プリセットシステム**:「デフォルト」ベースライン + ワンクリックのスタイルプリセット 4 種(顔文字 / アイルー / ネコ娘 / DeepSeekちゃん——タイトルと 5 終了理由 + 質問の文面一式をスタイル化)。現在の設定をカスタムプリセットとして保存可能(`localStorage` に永続化)。自動採番される無名プリセット(`未命名`、`未命名 2`…)、「出自:xxx · 変更あり」の出所表示、プリセット削除に対応。
85
+ - **プッシュタイトルテンプレート**:空欄の場合は各理由でデフォルトタイトルを使用(完了=タスク完了 / エラー=タスクエラー / … / 質問=AI からの質問が届きました)。`{title}` はセッションタイトルを参照します。
71
86
 
72
87
  ### 公式の口径と同じソース
73
88
 
@@ -222,6 +237,8 @@ dsh plugin --profile web remove @telosmaylx/dsh-session-notify
222
237
 
223
238
  **サブエージェントセッションはデフォルトでスキップ**(`header.origin === 'subagent'` または `delegationDepth > 0`)—— サブエージェントは親セッションがオーケストレーションするため、ターンごとの通知はノイズになります。ホスト設定でスキップを無効化できます。
224
239
 
240
+ **質問通知は独立したチャネルで、上記のホワイトリストには含まれません**:AI が `ask_user_question` を呼び出して回答を待つ間(`tool/call` イベント)、即座に通知し、`tool/result` が返ると通知は無効になります。質問はセッションログに書き込まれず、通知のみが表示されます。
241
+
225
242
  ### 通知本文の取得元
226
243
 
227
244
  クライアントはセッションリストで `running: true → false` のエッジを観測したときにプッシュします。本文は以下の優先順位で取得します(最大 6 秒のポーリング、400ms 間隔):
@@ -230,20 +247,22 @@ dsh plugin --profile web remove @telosmaylx/dsh-session-notify
230
247
  2. **セッションイベントウィンドウ内の notice ノード**(`kind=context` + `form=notice`)—— 表示中のセッションで、保存後すぐに利用可能。
231
248
  3. **フォールバック** —— 「詳細はセッション内のシステムメッセージを参照」+ ワークスペース情報(`cwd` の最後のセグメント)。
232
249
 
250
+ 質問通知の本文もホストプロジェクション(key = `session-complete-notify-question`、ホストがタイトルと本文をレンダリング済み)を優先します。古いホストにこのプロジェクションがない場合は、クライアントがタイトルと `{question}` テキストを自前で組み立てます。
251
+
233
252
  ### 通知の例
234
253
 
235
- 以下はすべて `lib/core.js` の `buildNotice` が実際に生成したものです。デフォルトメッセージは「セッション「{title}」〇〇。クリックして表示。」の形で統一(終了理由によって語彙が異なります。**所要時間・消費は含みません**):
236
-
237
- 日本語デフォルトメッセージ:
238
-
239
- ```text
240
- セッション「重构登录模块」完了。クリックして表示。 ← 完了
241
- セッション「重构登录模块」中止。クリックして表示。 ← 中止
242
- セッション「重构登录模块」がブロックされました。クリックして表示。 ← ブロック
243
- セッション「重构登录模块」上限に到達。クリックして表示。 ← 上限到達
244
- セッション「重构登录模块」エラー。クリックして表示。 ← エラー
245
- ```
246
-
254
+ 以下はすべて `lib/core.js` の `buildNotice` が実際に生成したものです。デフォルトメッセージは「セッション「{title}」〇〇。クリックして表示。」の形で統一(終了理由によって語彙が異なります。**所要時間・消費は含みません**):
255
+
256
+ 日本語デフォルトメッセージ:
257
+
258
+ ```text
259
+ セッション「重构登录模块」完了。クリックして表示。 ← 完了
260
+ セッション「重构登录模块」中止。クリックして表示。 ← 中止
261
+ セッション「重构登录模块」がブロックされました。クリックして表示。 ← ブロック
262
+ セッション「重构登录模块」上限に到達。クリックして表示。 ← 上限到達
263
+ セッション「重构登录模块」エラー。クリックして表示。 ← エラー
264
+ ```
265
+
247
266
  > セッションにタイトルがない場合(`titleValue` が空)は「セッション完了。クリックして表示。」にフォールバック。所要時間・消費・キャッシュヒット率・速度は、カスタムテンプレートで `{duration}` `{usage}` `{cache}` `{tps}` を挿入した場合のみ表示されます。
248
267
 
249
268
  カスタムテンプレート(設定パネルで編集。この例ではすべての情報枠を使用):
@@ -281,7 +300,7 @@ Session "重构登录模块" completed (took 3m25s, used 1,240 in / 3,560 out).
281
300
 
282
301
  ## 設定
283
302
 
284
- ほとんどの設定は **DSH Web UI → 設定 → プラグイン → セッション完了通知** パネルで行います(保存後、「クリックでリロード」を押すと有効になります)。「トリガー理由のホワイトリスト」と「サブエージェントをスキップ」の 2 項目のみ、ホストの `cordis.patch.yml` の `config` で設定します。
303
+ ほとんどの設定は **DSH Web UI → 設定 → プラグイン → セッション完了通知** パネルで行います(保存後、「クリックでリロード」を押すと有効になります)。「トリガー理由のホワイトリスト」のみ、ホストの `cordis.patch.yml` の `config` で設定します(サブエージェントのスキップはパネルのチェックボックスで制御)。
285
304
 
286
305
  ### 設定パネル
287
306
 
@@ -293,12 +312,12 @@ Session "重构登录模块" completed (took 3m25s, used 1,240 in / 3,560 out).
293
312
  | 言語 | 5 言語の単一選択。切り替えでパネル全体が即再レンダリング |
294
313
  | プッシュ方式 | 3 択:デュアルチャンネル(システム通知 + ページ内表示、デフォルト)/ システム通知のみ / ページ内表示のみ |
295
314
  | 通知メディア | 大きな画像の 2 つのソース:**理由ごとのアップロード**——テンプレート内で「+ 情報を挿入 → 画像」から `{image}` トークンを挿入しローカル画像を選択(エディタ内ではサムネイル付きチップとして表示、**512px 幅・通知表示比率 16:9 で中央クロップ**に自動圧縮、理由ごとに保存)。**グローバルの画像/アイコン**——2 つのアップロードカードを横並び 1 行に配置(**アイコンが先**。空欄は角丸の「+」タイルで、クリックでアップロード。**画像 512×288(16:9 中央クロップ)、アイコン 128×128(1:1 正方形中央クロップ)**。アップロード後はカードにサムネイルが表示され、**クリックで全画面プレビュー(等比・未クロップの元画像)**、右上の × で削除)。アイコンは空欄ならサイト既定のアイコン、またはテンプレートに `{icon}` トークンを挿入して**理由ごとのアイコン**を指定(グローバルより優先)。システム通知チャネルのみ有効(ページ内トーストはテキストカード)。「送信」テストボタンも同様に適用 |
296
- | タイトル | 折りたたみセクション(**既定で折りたたみ**、クリックで展開):**グローバルプッシュタイトル**(全理由共通。Chip エディタ——「+ 情報を挿入」で挿入した情報は**カプセルタグ**として表示され、クリックで削除。**通知送信時はタイトル内の情報トークン(所要時間/消費/エラー/キャッシュヒット/速度)が実際の値に置き換えられ、コードが露出しません**。空欄の場合は各理由でデフォルトタイトル——完了=タスク完了、エラー=タスクエラー、中止=タスク中止、ブロック=タスクブロック、上限=出力上限に到達)+ **理由ごとのタイトル**(5 つの理由それぞれに入力。空欄 = グローバルまたは言語デフォルトを使用) |
297
- | コンテンツ | 折りたたみセクション(**既定で折りたたみ**、クリックで展開)。展開すると各終了理由(完了、エラー、中止、ブロック、出力上限)ごとに**1 行レイアウト**(理由ラベル + Chip エディタ + 「+」挿入ボタン——メニュー展開中は「−」に変化 + **紙飛行機の送信ボタン**。ボタンは角丸矩形で垂直中央揃え):**テンプレートが空(既定プリセット)のときはエディタにデフォルト文面を表示**。テキスト + インライン情報カプセル、カーソル位置に挿入;`{image}`/`{icon}` チップは**サムネイルクリックで大図をプレビュー、× クリックでのみ削除**(誤削除防止)、他のチップはクリックで削除;**編集後に空にすると「空欄の場合はデフォルトの文面を使用」のプレースホルダーを表示(選択・削除不可)** |
315
+ | タイトル | 折りたたみセクション(**既定で折りたたみ**、クリックで展開):**グローバルプッシュタイトル**(全理由共通。Chip エディタ——「+ 情報を挿入」で挿入した情報は**カプセルタグ**として表示され、クリックで削除。**通知送信時はタイトル内の情報トークン(所要時間/消費/エラー/キャッシュヒット/速度)が実際の値に置き換えられ、コードが露出しません**。空欄の場合は各理由でデフォルトタイトル——完了=タスク完了、エラー=タスクエラー、中止=タスク中止、ブロック=タスクブロック、上限=出力上限に到達、質問=AI からの質問が届きました)+ **理由ごとのタイトル**(6 つの理由それぞれに入力。各行に「+」挿入ボタン——挿入可能な情報トークン(「質問」含む、画像/アイコン除く)、カーソル位置に挿入;**グローバルタイトルより優先**。空欄 = グローバルまたは言語デフォルトを使用) |
316
+ | コンテンツ | 折りたたみセクション(**既定で折りたたみ**、クリックで展開)。展開すると各理由(完了、エラー、中止、ブロック、出力上限、質問)ごとに**1 行レイアウト**(理由ラベル + Chip エディタ + 「+」挿入ボタン——メニュー展開中は「−」に変化 + **紙飛行機の送信ボタン**。ボタンは角丸矩形で垂直中央揃え):**テンプレートが空(既定プリセット)のときはエディタにデフォルト文面を表示**。テキスト + インライン情報カプセル、カーソル位置に挿入;`{image}`/`{icon}` チップは**サムネイルクリックで大図をプレビュー、× クリックでのみ削除**(誤削除防止)、他のチップはクリックで削除;**編集後に空にすると「空欄の場合はデフォルトの文面を使用」のプレースホルダーを表示(選択・削除不可)**。質問行のデフォルト文面は「AI からの質問:{question}」で、`{question}` は送信時に AI の実際の質問へ置き換わります(挿入メニューにも「質問」トークンがあり、他のトークンと同じ操作です) |
298
317
  | サブエージェントセッションをスキップ | チェックボックス(保存時に設定ドキュメントへ一緒に書き込み) |
299
318
  | 通知権限 | 状態をリアルタイム表示:許可済み(緑)/ 未許可(「許可をリクエスト」ボタン付き)/ ブラウザにブロック済み(アドレスバーの操作ガイド付き)/ 環境が非対応 |
300
319
  | 理由別のタイトルカスタマイズ | 折りたたみエリア(デフォルトで収納):終了理由ごとに独立したタイトル入力欄。空欄=グローバルテンプレートまたは言語デフォルトのタイトルを使用 |
301
- | 保存 | ホストの設定ドキュメントに書き込み(`language` / `templates` / `titleTemplate` / `titleTemplates` / `pushMode`)。保存後「クリックでリロード」リンクを表示 |
320
+ | 保存 | ホストの設定ドキュメントに書き込み(`language` / `templates` / `titleTemplate` / `titleTemplates` / `pushMode` / `skipSubagents`)。保存後「クリックでリロード」リンクを表示 |
302
321
  | リセット | 1 クリックでデフォルト値に復元(**言語は現在の選択を保持**、タイトル / テンプレート / プッシュ方式はデフォルトに戻す)し、即座に保存 |
303
322
 
304
323
  > [!NOTE]
@@ -324,6 +343,7 @@ Session "重构登录模块" completed (took 3m25s, used 1,240 in / 3,560 out).
324
343
  | `{tps}` | 生成速度(公式プロジェクション口径、データなしの場合は空) | `92 tok/s` |
325
344
  | `{image}` | カスタム通知イメージのスイッチ:「+ 情報を挿入」から挿入してローカル画像を選択(512px に自動圧縮)、理由ごとに独立。本文レンダリング時には剥除され、セッションログには書き込まれません。トークンを削除するとその理由の画像データも消去されます | — |
326
345
  | `{icon}` | カスタム通知アイコンのスイッチ:「+ 情報を挿入」から挿入してローカル画像を選択(128×128 正方形に自動圧縮)、理由ごとに独立。本文レンダリング時には剥除され、セッションログには書き込まれません。グローバルの「通知アイコン」より優先。トークンを削除するとその理由のアイコンデータも消去されます | — |
346
+ | `{question}` | **質問行専用のプレースホルダー**:送信時に AI の実際の質問文へ置き換えられます。「+ 情報を挿入」メニューと連動(「質問」トークンを直接選択可、手入力の `{question}` もカプセルとして認識)。質問チャネルでのみ使用可。他の理由行に挿入しても空文字に置き換えられます(リテラル漏れ防止) | `レポートの生成を続けますか?` |
327
347
  | `{label}` | 非推奨 —— レンダリング時に自動的に除去され、旧テンプレートとの互換性を維持(挿入メニューからは削除済み) | — |
328
348
 
329
349
  テンプレートを空欄にすると内蔵のデフォルトメッセージを使用します(「セッション「{title}」〇〇。クリックして表示。」の形で統一、所要時間と消費量は含みません)。折りたたみ行の `summary` は本文と同じソースです(レンダリング結果を 120 文字に切り詰め)—— 折りたたみ行だけを見るユーザーにも実際のタイトル・所要時間・消費量がわかります。
@@ -365,6 +385,7 @@ Session "重构登录模块" completed (took 3m25s, used 1,240 in / 3,560 out).
365
385
  │ session/event 火线 │
366
386
  │ ├─ turn/start → tracker 起表(key: sessionId:turn) │
367
387
  │ ├─ assistant/message → 累加该轮 token 用量 │
388
+ │ ├─ tool/call → ask_user_question?写提问投影(标题+正文) │
368
389
  │ └─ turn/end → reason.kind ∈ reasons ? │
369
390
  │ ├─ 子代理会话?跳过 │
370
391
  │ ├─ 读官方投影:cache / tps / title │
@@ -374,6 +395,8 @@ Session "重构登录模块" completed (took 3m25s, used 1,240 in / 3,560 out).
374
395
  │ │
375
396
  │ settings.register → 官方「设置 → 插件」命名空间(失败退避重试) │
376
397
  │ sessionProjections → 注册投影单元(key=session-complete-notify) │
398
+ │ + 提问投影(key=session-complete-notify- │
399
+ │ question,等待回答期间持续推送) │
377
400
  └──────────────────────────────┬──────────────────────────────────────┘
378
401
  │ user/message (source: plugin, form: notice)
379
402
  ▼ JSONL 持久化 + 投影推送
@@ -383,6 +406,8 @@ Session "重构登录模块" completed (took 3m25s, used 1,240 in / 3,560 out).
383
406
  │ ├─ 取正文:投影 → 事件窗口 notice → 降级(轮询 ≤6s) │
384
407
  │ ├─ Web Notification(独立 tag,点击聚焦) │
385
408
  │ └─ 页内 toast(永远展示,≤3 条,10s 自动消失) │
409
+ │ 提问投影轮询(key=session-complete-notify-question): │
410
+ │ 有值 → 立即弹提醒(标题+正文),无值清空 │
386
411
  │ │
387
412
  │ slots.inject('settings.plugin.item') → 设置卡片(预设/语言/模板) │
388
413
  └─────────────────────────────────────────────────────────────────────┘
@@ -410,7 +435,8 @@ dsh-session-notify/
410
435
  │ │ # sessionProjections 投影单元(后台会话推送正文)
411
436
  │ ├── core.js # 纯逻辑层(零依赖,可独立测试):轮次计时与用量聚合、
412
437
  │ │ # 5 语言文案表、时长/用量/缓存/速度格式化、
413
- │ │ # 模板渲染({title}{duration}{usage}{error}{cache}{tps}
438
+ │ │ # 模板渲染({title}{duration}{usage}{error}{cache}{tps})、
439
+ │ │ # 提问正文构建(buildQuestionBody,{question} + 媒体剥除)
414
440
  │ └── client.js # 浏览器平面:完成推送(系统通知 + toast)、
415
441
  │ # 设置卡片(Chip 模板编辑器 + 预设系统 + 实时预览)
416
442
  ├── scripts/
@@ -471,6 +497,13 @@ node scripts/verify-notice.mjs <session.jsonl.zstd>
471
497
 
472
498
  </details>
473
499
 
500
+ <details>
501
+ <summary><b>AI からの質問でもポップアップ通知されますか?</b></summary>
502
+
503
+ 通知されます。AI が `ask_user_question` を呼び出して回答を待つと、ホストは即座に「質問タイトル + 本文」を専用プロジェクション(key = `session-complete-notify-question`)へ書き込み、クライアントがポーリングで取得すると即座に通知します——別のページを見ていても見逃しません。質問の文言は完了通知と同じく完全にカスタマイズ可能です。設定パネルの「タイトル / コンテンツ」折りたたみセクションにそれぞれ「質問」行があり、本文は `{question}` プレースホルダー(AI の実際の質問を注入)と `{image}` / `{icon}` メディアスイッチに対応します。回答後(`tool/result`)は通知が無効になり、残りません。
504
+
505
+ </details>
506
+
474
507
  <details>
475
508
  <summary><b>「中断」(interrupted)で通知されないのはなぜ?</b></summary>
476
509
 
@@ -533,27 +566,31 @@ node scripts/verify-notice.mjs <session.jsonl.zstd>
533
566
 
534
567
  ---
535
568
 
536
- ## 更新履歴
537
-
538
- | バージョン | 日付 | 変更内容 |
539
- | --- | --- | --- |
540
- | **0.1.13** | 2026-08-29 | **ワンクリックスタイルプリセット 4 種**を追加(顔文字 / アイルー / ネコ娘 / DeepSeekちゃん——タイトル + 5 理由の文面一式をスタイル化、**5 言語対応**・読み込み時に UI 言語を強制変更しない)。プリセットドロップダウンから廃止済みの旧テンプレート名(playful/formal/detail)を整理。README のデフォルトメッセージ例を「セッション「{title}」〇〇。クリックして表示。」の形に同期(所要時間と消費量は含まない) |
541
- | **0.1.12** | 2026-08-29 | パッケージ整理:scripts に混入した一時監査スクリプトを除去 |
542
- | **0.1.11** | 2026-08-29 | **カスタム通知メディア**を追加:① テンプレートの「+ 情報を挿入」メニューに**画像**トークンを追加——`{image}` を挿入してローカル画像を選択(**512px 幅・通知表示比率 16:9 で中央クロップ**に自動圧縮、エディタ内ではサムネイル付きチップとして表示)、理由ごとにアップロードして設定ドキュメントに保存(本文レンダリング時に剥除され、セッションログには書き込まれません)。② **通知イメージ/アイコンがアップロードカードに変更**(空欄は角丸の「+」タイルで、クリックでアップロード。**画像 512×288(16:9 中央クロップ)、アイコン 128×128(1:1 正方形中央クロップ)**。アップロード後はカードにサムネイルが表示され、右上の × で削除)——クロップにより、アップロードした画像が通知カードに完全に表示され、システムの表示領域による切り詰めを防ぎます。③ **プッシュタイトルの「+ 情報を挿入」**で任意の情報トークンを挿入(セッションタイトル/所要時間/消費/キャッシュヒット/速度)。設定パネルの「通知メディア」エリアに**処理方法の説明**(クロップ比率)と**アップロード後のサムネイルプレビュー**を表示。④ **理由ごとの通知アイコン**(テンプレートに `{icon}` トークンを挿入 + ローカルアップロード、128×128 正方形、グローバルより優先)。⑤ **`{image}`/`{icon}` トークンを削除するとその理由の画像/アイコンデータも消去**。⑥ **レイアウト改善:「本文テンプレート × 5」を折りたたみセクションに変更(既定で折りたたみ、パネルをコンパクトに。ヘッダーにカスタマイズ済みの数を表示、クリックで展開)**。⑦ **レイアウト改善②:通知イメージ/アイコンのアップロードカードを横並び 1 行に配置(間に余白);プッシュタイトルと理由ごとのタイトルを「タイトル」の折りたたみセクションに統合(既定で折りたたみ);折りたたみインジケーターを三角形アイコンに変更(「展開/折りたたむ」の文言を削除、i18n 負担を軽減)**。⑧ **操作改善:プッシュタイトルを Chip エディタに変更(挿入した情報はカプセルタグとして表示され、`{title}` などのコードが露出しない);画像/アイコンのカード順を入れ替え(アイコンが先);「本文テンプレート × 5」を「コンテンツ」に改名;画像/アイコンのサムネイルクリックで全画面プレビュー(ライトボックス)、タグは × クリックでのみ削除(誤削除防止)**。⑨ **プレビューを元画像に統一:アップロード時にクロップ版(通知用)と等比の完全版(1024px、ライトボックスの原図表示用)の両方を保存し、カードとタグの拡大プレビューはいずれも未クロップの元画像を表示。折りたたみの三角形アイコンを拡大**。⑩ **タイトルの情報トークン修正:通知タイトル内の `{duration}` `{usage}` `{error}` `{cache}` `{tps}` はセッションの実際の値に置き換えられ、コードが露出しません(編集時はカプセル、送信時は実データ)。「コンテンツ」を 1 行レイアウトに変更(理由ラベル + エディタ + 「+/-」挿入ボタン + 紙飛行機の送信ボタン)**。⑪ **詳細:コンテンツ行下のリアルタイムプレビューを削除(よりコンパクトに);コンテンツのエディタ内余白を縮小(compact);「+/-」と紙飛行機ボタンを角丸矩形枠・垂直中央揃えに変更**。⑫ **削除体験の修正:コンテンツを空にしてもデフォルト文面に戻らず(プレースホルダーを表示)、空にした後はカーソルが末尾に戻るため Backspace 長押しで連続削除可能;「+/-」を SVG 線アイコンに変更(ボタン枠に正確に中央揃え);紙飛行機アイコンを反時計回りに 30° 回転**。⑬ **プレースホルダーとデフォルト文面:テンプレートが空(既定プリセット)のときはエディタにデフォルト文面を直接表示(見たままがそのまま通知されます)。編集後に空にすると「空欄の場合はデフォルトの文面を使用」のプレースホルダーを表示——CSS 疑似要素で実装(ネイティブの input placeholder と同様:選択・削除不可)、従来のコピー可能なプレースホルダー文字列を置き換え**。⑭ **詳細②:プッシュタイトルのプレースホルダー文言を「共通のプッシュタイトル。空欄の場合はデフォルトタイトルを使用(下の理由別タイトルが優先)」に変更、空エディタをクリックするとカーソルが先頭に移動(入力文字が左端から始まる、ネイティブ placeholder と同様);紙飛行機アイコンをさらに反時計回りに 30° 回転(合計 60°);「コンテンツ」の理由ラベルを自動幅に変更(エディタがラベル文字に密着);エディタの高さを統一(box-sizing border-box + min-height 38px、通常の入力欄と同様)**。⑮ **詳細③:理由ラベルを固定 70px 幅に統一(コンテンツと理由別タイトルで一致);Backspace/Delete でのチップ削除を手動処理に変更し、カーソルを削除位置に復元(エディタ先頭に飛ばない);送信ボタンのアイコンをベル(プッシュ通知の意味)に変更;カスタムプリセットが通知イメージ/アイコン(完全版プレビュー含む)も保存するようになり、既定プリセットは空欄のまま**。⑯ **詳細④:通知イメージ/アイコンのアップロードカードを等高に(統一 64px 高、大図 16:9 は幅 114px);理由ラベルの幅を 70px → 60px に;送信ボタンのアイコンをベルから右向きのプッシュ矢印に変更;「タイトル」「コンテンツ」の折りたたみヘッダー文字を太字に**。⑰ **デフォルト文面とラベル:max-tokens の表示を「上限」に統一(ラベル/デフォルトタイトル/プレースホルダー、5 言語で同期);各理由のデフォルト文面を「セッション「{title}」完了。クリックして表示。」に変更(5 言語、エディタのヒントとホスト側レンダリングを同期);理由別タイトルの各行に「+」挿入ボタンを追加(プッシュタイトルと同様:情報トークンのみ、画像/アイコンなし、カーソル位置に挿入)**。⑱ **タグ化とプリセット管理:理由別タイトルを Chip エディタに変更(挿入した情報はカプセルタグとして表示され、{title} などのコードが露出しない;{image}{icon} は禁止——手入力時はリテラルテキストのまま、送信時に剥除);カスタムプリセットに「名前を変更」機能を追加(カスタムプリセット選択時に表示、変更後はドロップダウンと現在のプリセットが同期)**。⑲ **詳細⑤:Chip エディタの文字を垂直中央に配置(行の高さとカプセルを 20px に統一、上下パディング 9px、38px ボックス内で単行テキストが中央に表示);「タイトル」「コンテンツ」の折りたたみバーに背景色を追加(ヘッダーは角丸の色付きバー、展開領域は左ボーダー付きインデント);5 言語 86 キーの i18n を全面的に確認(すべて翻訳済み、zh-TW「名前を変更」と ko「破棄」の表現を修正)**。「送信」テストボタンも同様に適用。システム通知チャネルのみ有効(ページ内トーストはテキストカード);⑳ **詳細⑥:「タイトル」「コンテンツ」の折りたたみヘッダーの背景塗りを削除;プレースホルダー文字色を rgba(127,127,127,0.5) に変更(より淡く、テーマ変数の影響を受けない)**。「送信」テストボタンも同様に適用。システム通知チャネルのみ有効(ページ内トーストはテキストカード);㉑ **修正:「コンテンツ」の各理由行間の区切り線を削除;「コンテンツ」を展開した際のヘッダー直下の線を「タイトル」と同様(全幅)に補完;Backspace/Delete でのチップ削除時、カーソルが先頭に飛ばないよう修正(要素コンテナ内では子ノードのインデックスからチップを特定、チップ直後・エディタ末尾のケースを含む);「未命名」プリセットの名前変更時は自動的に具名プリセットへ昇格(新しい id に置換)し、ドロップダウンに即座に新名を表示**。「送信」テストボタンも同様に適用。システム通知チャネルのみ有効(ページ内トーストはテキストカード);㉒ **修正:文字入力後にプレースホルダーが残らないように(プレースホルダー CSS セレクタと状態属性を整合、入力すると即消え、文字は左端から);画像/アイコンのアップロード・削除後もカーソル位置を維持(DOM 再構築前にカーソルオフセットを保存し、再構築後に復元);プリセットのドロップダウンをフォーカスしても白い選択状態の枠線が出ないように**。「送信」テストボタンも同様に適用。システム通知チャネルのみ有効(ページ内トーストはテキストカード);㉓ **修正:チップ(画像/アイコン含む)削除後もカーソルが飛ばず消えず、連続削除が可能に——画像データ変更による再構築を必要なときだけ実行(DOM 内に対応するメディアチップがなくテンプレートにも該当タグがない場合は再構築をスキップし、カーソルを削除位置に維持。アップロード/置き換えでサムネイル更新が必要な場合のみ再構築)、カーソルオフセット計算も修正(チップは一律 1 位置として数え、内部文字には入らない)**。「送信」テストボタンも同様に適用。システム通知チャネルのみ有効(ページ内トーストはテキストカード) |
543
- | **0.1.10** | 2026-08-29 | 「プッシュタイトル」をネイティブ入力欄に変更(ネイティブのプレースホルダー:コピー不可、入力すると消え、空にすると復元。「+ セッションタイトル」でカーソル位置に `{title}` を挿入)。ドキュメントに QQ ブラウザの内蔵プッシュポップアップの説明を追記(3 つのプッシュ方式の実際の挙動 + 送信ボタンのテストも同じ規則) |
544
- | **0.1.9** | 2026-08-29 | プッシュタイトルが**理由別のカスタマイズ**に対応(折りたたみエリア UI、デフォルトで収納され冗長にならない。空欄時は各理由で異なるデフォルトタイトル:タスク完了 / タスクエラー / タスク中止 / タスクブロック / タスクが出力上限に到達、5 言語)。プロジェクションをオブジェクト(kind/text/title)にアップグレードし、host がレンダリングしたタイトルを保持。リセットボタンが**現在の言語を保持**。デフォルトメッセージに「セッションタイトル」ラベルを埋め込み(セッション「{title}」が完了、タイトルなしは自動フォールバック)。設定パネルのテンプレートプレビューを同期。「+ 情報を挿入」でラベル挿入後に自動折りたたみしなくなった。使用中のカスタムプリセットを削除すると自動的にデフォルトへ戻る。テンプレートプレビューを修正(クリックで消えない、入力でのみ非表示、空にすると復元)。各理由に「送信」ボタンを追加(現在のテンプレートでレンダリングしたテスト通知を 1 クリックで送信) |
545
- | **0.1.8** | 2026-08-29 | デフォルトのプッシュタイトルを「タスク完了」に変更(`{title}` は引き続きセッションタイトルを参照可)。デフォルトメッセージを終了理由ごとに異なる表現に(完了=コンパクトな括弧式 / 中止・ブロック=文を分ける / エラー=エラー前置 / 上限=アドバイス付き、5 言語)。設定パネルに「リセット」ボタンを追加し、1 クリックでデフォルトに復元 |
546
- | **0.1.7** | 2026-08-29 | 0.1.6 の設定カードのクラッシュを修正:`notificationPermissionRow`/`requestPermissionNow` Card コンポーネント内の state(スコープ外)を参照していたため、レンダリング時に ReferenceError が発生し、設定カード全体が消えていた。自己完結型 + コールバックで引数を渡す方式に変更 |
547
- | **0.1.6** | 2026-08-29 | 設定パネルに「通知権限」ステータスエリアを追加(許可状態のリアルタイム表示 + 1 クリック権限リクエストボタン + ブロック時のアドレスバー操作ガイド)。権限リクエストを**ユーザージェスチャ内での要求**に変更(Chromium はジェスチャ以外の自動リクエストを無視する。Edge が馴染みのないサイトで通知を自動ブロックする典型的なシナリオを解決)。FAQ にブラウザ差分の説明を追加 |
548
- | **0.1.5** | 2026-08-29 | 「プッシュ方式」設定を追加(デュアルチャンネル / システム通知のみ / ページ内表示のみ)。QQ ブラウザなどの Chromium シェルが `Notification` をページ内バナーとしてレンダリングすることによる二重通知を解決。`pushMode` を設定 schema と設定パネルに追加 |
549
- | **0.1.4** | 2026-08-28 | 完全なアンインストール対応を追加:`dispose` ライフサイクルの終了処理(host はアンインストールフラグを立て、保留中のマイクロタスク追加を抑制。client は本文ポーリングタイマー、`__dsch_notify_debug` フック、toast コンテナをクリーンアップ)。アンインストールのドキュメントと FAQ を同期 |
550
- | **0.1.3** | 2026-08-28 | 公式 `dsh.bundle` manifest を宣言(`dsh plugin add` の 1 コマンドで自動マウント)。settings リトライタイマーを `ctx.effect()` ラップに変更(Cordis effect の規律)。インストールドキュメントを再編成 |
551
- | 0.1.2 | 2026-08-27 | パッケージ名を `@telosmaylx` scope に変更(npm ユーザー名のスコープ) |
552
- | 0.1.1 | 2026-08-27 | GitHub、npm のインストール方法をドキュメント化 |
553
- | 0.1.0 | 2026-08-26 | 初期バージョン:セッション内システムメッセージ + ブラウザプッシュ + 公式設定パネル |
554
-
555
- ---
556
-
569
+ ## 更新履歴
570
+
571
+ | バージョン | 日付 | 変更内容 |
572
+ | --- | --- | --- |
573
+ | **0.1.17** | 2026-08-30 | **質問の即時通知(カスタマイズ版)**:AI の質問で即ポップアップ。質問文言は `{question}` プレースホルダーとメディアスイッチに対応。4 プリセットに 5 言語の質問文言を追加。旧ホストではフォールバック |
574
+ | **0.1.16** | 2026-08-30 | **操作修正**:Backspace 連打でタグを誤削除しなくなりました(カーソルとタグの間に文字がないときだけタグを削除) |
575
+ | **0.1.15** | 2026-08-30 | **操作改善**:「コンテンツ」をデフォルトで展開。タグ削除後、カーソルが実際の内容に直行し連続削除が可能に |
576
+ | **0.1.14** | 2026-08-30 | **コードレビュー修正**:プリセット削除確認、保存済み設定への復元、メディア「×」でプレビューも削除、プリセット照合に画像を含める、同名警告、変更時のみリセット可能、デバッグログ自動切り詰め |
577
+ | **0.1.13** | 2026-08-29 | ワンクリックスタイルプリセット 4 種追加(顔文字/アイルー/ネコ娘/DeepSeekちゃん)、5 言語対応 |
578
+ | **0.1.12** | 2026-08-29 | リリースパッケージの整理 |
579
+ | **0.1.11** | 2026-08-29 | **カスタム通知メディア**:`{image}`/`{icon}` トークン挿入と画像/アイコンのアップロード(自動クロップ)。タイトルに情報プレースホルダー。「本文テンプレート×5」を折りたたみに。レイアウト・操作を全面改善 |
580
+ | **0.1.10** | 2026-08-29 | プッシュタイトルをネイティブ入力に。多言語 README 追加(English/繁體/日本語/한국어) |
581
+ | **0.1.9** | 2026-08-29 | 理由別プッシュタイトル。投影をオブジェクト化。リセットで言語を維持。理由別「送信」テストボタン |
582
+ | **0.1.8** | 2026-08-29 | デフォルトタイトル「タスク完了」。理由別デフォルト文言。リセットボタン追加 |
583
+ | **0.1.7** | 2026-08-29 | 設定カードのクラッシュ修正(通知権限行のスコープ問題) |
584
+ | **0.1.6** | 2026-08-29 | 通知権限ステータス領域を追加。権限リクエストをユーザージェスチャー内に変更 |
585
+ | **0.1.5** | 2026-08-29 | 通知チャネル設定(二重/システムのみ/ページ内のみ)——QQ ブラウザの二重通知を解消 |
586
+ | **0.1.4** | 2026-08-28 | 完全なアンインストール対応(dispose ライフサイクル整理) |
587
+ | **0.1.3** | 2026-08-28 | dsh.bundle manifest を宣言。settings 再試行タイマーを ctx.effect() に |
588
+ | 0.1.2 | 2026-08-27 | `@telosmaylx` スコープに改名 |
589
+ | 0.1.1 | 2026-08-27 | GitHub / npm インストール方法を文書化 |
590
+ | 0.1.0 | 2026-08-26 | 初版:セッション内システムメッセージ + ブラウザ通知 + 公式設定パネル |
591
+
592
+ ---
593
+
557
594
  ## コントリビューション
558
595
 
559
596
  Issue と PR をお待ちしています: