yt-briefing 0.15.0 → 0.15.1

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.
@@ -36,6 +36,7 @@ while true:
36
36
  - **A.** Take `out.summary` (markdown) and `out.pending` (metadata).
37
37
  - **B.** In the same turn, as **your chat text** (NOT command output — the UI does not show it), paste `summary` **verbatim**: no paraphrase, no shortening, no comment, no "see above". The user must see it before the popup. B is **unconditional** — it does not depend on there being anything else to write, so an iteration that returns no `skipped` line still opens with the pasted summary, never straight with the tool call. _(If the user says "I don't see the summary" — you skipped B. In Claude Code a PreToolUse gate refuses to record the rating in step D when the pending video's id is missing from your chat text; if it fires, paste the summary, ask for the rating again, then record it.)_
38
38
  - **C.** In the same message call `AskUserQuestion` — **1 call, 1 question** (everything in one step), phrased in `output_lang`:
39
+ - **Put the summary into the question text too**, opening with the channel, title and video id — e.g. `@channel — "title" (videoId, type)` followed by the summary points. Some clients don't show chat text above an open popup, so the popup is what the user actually reads; it is also what the gate in step D accepts as proof when the chat text reaches the transcript late.
39
40
  - The question (e.g. "Rating?") with three options whose descriptions explain: **OK** = neutral (no effect on the filter), **Weak** = worthless (teach the filter to skip such titles), **Research** = break the loop and dig into this video's content together. The digits never appear in the popup — internally map to `--rating`: **OK → 1, Weak → 0**; Research maps to no digit, it exits the loop (see Research mode). There is **no positive rating** — keeping the channel is the implicit positive; you only down-rate noise (`0`) or steer with a comment.
40
41
  - **Other** is the comment / stop / research channel (no second question): the user types free text. If it equals `stop` (case-insensitive, trimmed) — or the popup is dismissed (✕) — **end the loop**. If it **starts with `?`**, the text after the `?` is a **research question** → Research mode. Otherwise it is a **comment**: **distill** the user's raw text into a clean, generalizable rule, and infer the rating — clearly negative → `0`, otherwise → `1`.
41
42
  - **D.** Act on the answer:
@@ -19,9 +19,19 @@ export function isRatingWrite(payload) {
19
19
  return command.includes(RATING_SCRIPT) && command.includes('--rating');
20
20
  }
21
21
  /**
22
- * Did the agent actually paste the summary? The summary carries the video's watch URL, so its id
23
- * appears verbatim in the pasted text. Only the assistant's own text blocks count: the id also
24
- * travels through tool calls and their results, and neither is shown to the user.
22
+ * Did the user actually see the summary? The summary carries the video's watch URL, so its id
23
+ * appears verbatim wherever it was shown. Two surfaces count:
24
+ *
25
+ * - the assistant's own text blocks (step B, the paste above the popup);
26
+ * - the `AskUserQuestion` call itself — the popup's question text is what the user reads while
27
+ * rating, and some clients don't show chat text above an open popup at all.
28
+ *
29
+ * The popup is also the only surface that is reliably on disk in time: the harness can persist an
30
+ * assistant's text blocks late — in background sessions only once the turn ends — while the
31
+ * popup's tool_use entry is written as soon as the call is made. Gating on text alone blocked
32
+ * ratings whose summary the user had plainly read (measured 2026-10-04: the popup entry carrying
33
+ * the id was in the transcript, the text block with the same id was not, across several messages
34
+ * of one turn). Other tool calls and all tool results still don't count — the user sees neither.
25
35
  */
26
36
  export function summaryWasPasted(transcript, videoId) {
27
37
  for (const line of transcript.split('\n')) {
@@ -36,8 +46,16 @@ export function summaryWasPasted(transcript, videoId) {
36
46
  }
37
47
  if (entry.message?.role !== 'assistant' || !Array.isArray(entry.message.content))
38
48
  continue;
39
- if (entry.message.content.some((b) => b.type === 'text' && b.text?.includes(videoId)))
49
+ if (entry.message.content.some((b) => shownToUser(b, videoId)))
40
50
  return true;
41
51
  }
42
52
  return false;
43
53
  }
54
+ function shownToUser(block, videoId) {
55
+ if (block.type === 'text')
56
+ return block.text?.includes(videoId) ?? false;
57
+ if (block.type === 'tool_use' && block.name === 'AskUserQuestion') {
58
+ return JSON.stringify(block.input ?? {}).includes(videoId);
59
+ }
60
+ return false;
61
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yt-briefing",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
4
4
  "description": "A self-learning YouTube briefing engine: it sweeps the channels you follow, filters noise in two stages (title, then transcript), summarizes the rest in your language, and adapts to your ratings — one video at a time.",
5
5
  "type": "module",
6
6
  "bin": {