tailii-host 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/dist/approval/hook.js +11 -1
  2. package/dist/approval/hook.js.map +1 -1
  3. package/dist/backend/herdr.d.ts +30 -1
  4. package/dist/backend/herdr.js +103 -20
  5. package/dist/backend/herdr.js.map +1 -1
  6. package/dist/backend/sessionBackend.d.ts +12 -0
  7. package/dist/backend/sessionBackend.js +6 -0
  8. package/dist/backend/sessionBackend.js.map +1 -1
  9. package/dist/backend/tmux.d.ts +153 -3
  10. package/dist/backend/tmux.js +488 -14
  11. package/dist/backend/tmux.js.map +1 -1
  12. package/dist/chat/chatTailController.d.ts +3 -1
  13. package/dist/chat/chatTailController.js +3 -0
  14. package/dist/chat/chatTailController.js.map +1 -1
  15. package/dist/chat/subagentTailer.d.ts +51 -0
  16. package/dist/chat/subagentTailer.js +491 -47
  17. package/dist/chat/subagentTailer.js.map +1 -1
  18. package/dist/chat/subagentTranscript.js +51 -5
  19. package/dist/chat/subagentTranscript.js.map +1 -1
  20. package/dist/chat/transcriptTailer.d.ts +37 -1
  21. package/dist/chat/transcriptTailer.js +170 -3
  22. package/dist/chat/transcriptTailer.js.map +1 -1
  23. package/dist/codex/codexAppServer.js +20 -0
  24. package/dist/codex/codexAppServer.js.map +1 -1
  25. package/dist/codex/codexNativeTurnController.js +20 -4
  26. package/dist/codex/codexNativeTurnController.js.map +1 -1
  27. package/dist/codex/codexRolloutTailer.d.ts +1 -0
  28. package/dist/codex/codexRolloutTailer.js +7 -0
  29. package/dist/codex/codexRolloutTailer.js.map +1 -1
  30. package/dist/codex/codexSystemNotice.d.ts +16 -0
  31. package/dist/codex/codexSystemNotice.js +195 -0
  32. package/dist/codex/codexSystemNotice.js.map +1 -0
  33. package/dist/commands/doctor.d.ts +2 -6
  34. package/dist/commands/doctor.js +2 -29
  35. package/dist/commands/doctor.js.map +1 -1
  36. package/dist/commands/launch.d.ts +2 -0
  37. package/dist/commands/launch.js +5 -0
  38. package/dist/commands/launch.js.map +1 -1
  39. package/dist/engine/engine.js +32 -22
  40. package/dist/engine/engine.js.map +1 -1
  41. package/dist/engine/handlers/accountUsage.d.ts +17 -0
  42. package/dist/engine/handlers/accountUsage.js +40 -6
  43. package/dist/engine/handlers/accountUsage.js.map +1 -1
  44. package/dist/engine/handlers/mode.js +42 -1
  45. package/dist/engine/handlers/mode.js.map +1 -1
  46. package/dist/engine/handlers/session.js +5 -3
  47. package/dist/engine/handlers/session.js.map +1 -1
  48. package/dist/engine/handlers/usage.js +4 -1
  49. package/dist/engine/handlers/usage.js.map +1 -1
  50. package/dist/engine/handlers/workspace.js +38 -1
  51. package/dist/engine/handlers/workspace.js.map +1 -1
  52. package/dist/hub/effortDialog.d.ts +20 -0
  53. package/dist/hub/effortDialog.js +40 -0
  54. package/dist/hub/effortDialog.js.map +1 -0
  55. package/dist/hub/engineRelaySocket.d.ts +3 -0
  56. package/dist/hub/engineRelaySocket.js +2 -1
  57. package/dist/hub/engineRelaySocket.js.map +1 -1
  58. package/dist/hub/hubDaemon.js +20 -3
  59. package/dist/hub/hubDaemon.js.map +1 -1
  60. package/dist/hub/hubProtocol.js +8 -2
  61. package/dist/hub/hubProtocol.js.map +1 -1
  62. package/dist/hub/panePreviewPump.d.ts +27 -0
  63. package/dist/hub/panePreviewPump.js +62 -1
  64. package/dist/hub/panePreviewPump.js.map +1 -1
  65. package/dist/hub/questionInjection.js +2 -1
  66. package/dist/hub/questionInjection.js.map +1 -1
  67. package/dist/hub/reaper.d.ts +5 -0
  68. package/dist/hub/reaper.js +25 -7
  69. package/dist/hub/reaper.js.map +1 -1
  70. package/dist/hub/sessionHub.d.ts +48 -1
  71. package/dist/hub/sessionHub.js +248 -25
  72. package/dist/hub/sessionHub.js.map +1 -1
  73. package/dist/protocol/decode.js +45 -0
  74. package/dist/protocol/decode.js.map +1 -1
  75. package/dist/protocol/encode.js +2 -0
  76. package/dist/protocol/encode.js.map +1 -1
  77. package/dist/protocol/messages.d.ts +65 -1
  78. package/dist/services/accountIdentity.js +3 -1
  79. package/dist/services/accountIdentity.js.map +1 -1
  80. package/dist/services/fileService.d.ts +19 -1
  81. package/dist/services/fileService.js +126 -0
  82. package/dist/services/fileService.js.map +1 -1
  83. package/dist/services/keychainMirror.d.ts +9 -0
  84. package/dist/services/keychainMirror.js +64 -0
  85. package/dist/services/keychainMirror.js.map +1 -0
  86. package/dist/services/officialApps.d.ts +12 -0
  87. package/dist/services/officialApps.js +84 -30
  88. package/dist/services/officialApps.js.map +1 -1
  89. package/dist/services/planUsageFetcher.d.ts +53 -5
  90. package/dist/services/planUsageFetcher.js +109 -22
  91. package/dist/services/planUsageFetcher.js.map +1 -1
  92. package/dist/sessions/claudeSessionStore.js +30 -0
  93. package/dist/sessions/claudeSessionStore.js.map +1 -1
  94. package/dist/sessions/heartbeat.d.ts +7 -1
  95. package/dist/sessions/heartbeat.js +3 -1
  96. package/dist/sessions/heartbeat.js.map +1 -1
  97. package/dist/sessions/sessionSearch.js +23 -5
  98. package/dist/sessions/sessionSearch.js.map +1 -1
  99. package/dist/shared/cliVersions.d.ts +14 -0
  100. package/dist/shared/cliVersions.js +38 -0
  101. package/dist/shared/cliVersions.js.map +1 -0
  102. package/dist/shared/crossSession.d.ts +15 -0
  103. package/dist/shared/crossSession.js +75 -0
  104. package/dist/shared/crossSession.js.map +1 -0
  105. package/dist/shared/harnessReminder.d.ts +32 -0
  106. package/dist/shared/harnessReminder.js +124 -0
  107. package/dist/shared/harnessReminder.js.map +1 -0
  108. package/dist/shared/lineWriter.d.ts +7 -0
  109. package/dist/shared/lineWriter.js +26 -0
  110. package/dist/shared/lineWriter.js.map +1 -1
  111. package/dist/shared/systemNotice.d.ts +30 -0
  112. package/dist/shared/systemNotice.js +209 -0
  113. package/dist/shared/systemNotice.js.map +1 -0
  114. package/package.json +3 -3
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 外部 CLI(Claude Code / Codex)との互換性境界。
3
+ *
4
+ * Tailii が依存している/相互運用を確認済みの最低版。最新版追従ではなく下限のみを
5
+ * 定め、上限は設けない(CLI は自動更新されるため、完全一致の許可リストは更新の度に
6
+ * 機能停止した実績がある)。新版の妥当性は各機能の実行時検証に委ねる。
7
+ * doctor(診断)と officialApps(公式アプリ連携)の双方がこの値を参照する。
8
+ */
9
+ export const MINIMUM_CLAUDE_CLI_VERSION = "2.1.215";
10
+ export const MINIMUM_CODEX_CLI_VERSION = "0.144.5";
11
+ /** `--version` の先頭に現れる数値版を比較用の整数列へ変換する。 */
12
+ export function parseNumericVersion(raw) {
13
+ if (raw === null)
14
+ return null;
15
+ const match = /\d+(?:\.\d+)+/.exec(raw);
16
+ if (match === null)
17
+ return null;
18
+ const parts = match[0].split(".").map(Number);
19
+ return parts.every(Number.isFinite) ? parts : null;
20
+ }
21
+ /** prerelease の飾りは無視し、数値成分だけで `actual >= minimum` を判定する。 */
22
+ export function versionAtLeast(actual, minimum) {
23
+ const left = parseNumericVersion(actual);
24
+ const right = parseNumericVersion(minimum);
25
+ if (left === null || right === null)
26
+ return null;
27
+ const length = Math.max(left.length, right.length);
28
+ for (let index = 0; index < length; index += 1) {
29
+ const l = left[index] ?? 0;
30
+ const r = right[index] ?? 0;
31
+ if (l > r)
32
+ return true;
33
+ if (l < r)
34
+ return false;
35
+ }
36
+ return true;
37
+ }
38
+ //# sourceMappingURL=cliVersions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cliVersions.js","sourceRoot":"","sources":["../../src/shared/cliVersions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,SAAS,CAAC;AACpD,MAAM,CAAC,MAAM,yBAAyB,GAAG,SAAS,CAAC;AAEnD,2CAA2C;AAC3C,MAAM,UAAU,mBAAmB,CAAC,GAAkB;IACpD,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9C,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AACrD,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,cAAc,CAAC,MAAqB,EAAE,OAAe;IACnE,MAAM,IAAI,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC3C,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACjD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACnD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC/C,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QACvB,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;IAC1B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,15 @@
1
+ export interface CrossSessionMessage {
2
+ /** 送信元セッション名(`from-name` 属性)。属性なし/空は null。 */
3
+ senderName: string | null;
4
+ /** 封筒の中身(前後の空白は除去済み)。 */
5
+ body: string;
6
+ }
7
+ /**
8
+ * text が別セッションからのメッセージ封筒なら送信元名と本文を返す。封筒でなければ null。
9
+ * 閉じタグが無い場合は末尾までを本文とする。封筒より後ろ(取り扱いガイダンス)は落とす。
10
+ */
11
+ export declare function presentCrossSessionMessage(text: string): CrossSessionMessage | null;
12
+ /** 表示用の送信元ラベル(名前が無い封筒のフォールバック込み)。 */
13
+ export declare function crossSessionSenderLabel(message: CrossSessionMessage): string;
14
+ /** 一覧プレビュー/タイトル/検索スニペット向けの転写(「⇄ 送信元名: 本文」。本文なしは名前だけ)。 */
15
+ export declare function crossSessionPreviewLine(message: CrossSessionMessage): string;
@@ -0,0 +1,75 @@
1
+ // 別セッションからのメッセージ封筒(<cross-session-message …>)の転写。
2
+ //
3
+ // Claude Code のセッション間メッセージ(SendMessage)は、受信側 transcript へ 2 形で残る:
4
+ // ① idle 起こし: user 行(isMeta)。前置き 1 行 "Another Claude session sent a message:" +
5
+ // 封筒 + 封筒の後ろに取り扱いガイダンスの英文段落。
6
+ // ② ターン処理中: attachment(queued_command) の prompt(封筒のみ。前置き/後置きなし)。
7
+ // どちらも本文は
8
+ // <cross-session-message from="uds:/…" from-name="bay-3d" from-mode="prompting">
9
+ // 本文…
10
+ // </cross-session-message>
11
+ // の行構造。生 XML を表示へ出さないよう、from-name と本文だけを取り出す。
12
+ //
13
+ // 会話画面の転写は iOS 側 `ChatLogModel.crossSessionMessage()` が同じ規則で行う。本 helper は
14
+ // 一覧プレビュー/タイトル(claudeSessionStore)・会話検索(sessionSearch)・サブエージェント
15
+ // transcript ビューア(subagentTranscript)で共有する。判定の門番は harnessReminder と同じ流儀:
16
+ // 封筒が(既知の前置き行を除き)先頭から始まる場合だけ反応し、本文途中の言及・引用には
17
+ // 反応しない。行比較は行末の `\r` を除いた本体で行う(CRLF 耐性)。
18
+ const OPEN_PREFIX = "<cross-session-message";
19
+ const CLOSER = "</cross-session-message>";
20
+ /**
21
+ * idle 起こし形(①)の前置き行。実測は "Another Claude session sent a message:"。
22
+ * 文言はバージョンで揺れ得るため(reply 等)、"Another Claude …:" の 1 行として許容する。
23
+ */
24
+ const WAKE_PREFIX = /^Another Claude .*:$/u;
25
+ /**
26
+ * text が別セッションからのメッセージ封筒なら送信元名と本文を返す。封筒でなければ null。
27
+ * 閉じタグが無い場合は末尾までを本文とする。封筒より後ろ(取り扱いガイダンス)は落とす。
28
+ */
29
+ export function presentCrossSessionMessage(text) {
30
+ if (!text.includes(OPEN_PREFIX))
31
+ return null;
32
+ const lines = text.split("\n");
33
+ const core = (line) => (line.endsWith("\r") ? line.slice(0, -1) : line);
34
+ // 先頭の空行と(1 回だけ)前置き行を読み飛ばし、封筒の開始行へ到達するか確認する。
35
+ let index = 0;
36
+ let sawWakePrefix = false;
37
+ while (index < lines.length) {
38
+ const line = core(lines[index] ?? "").trim();
39
+ if (line === "") {
40
+ index += 1;
41
+ continue;
42
+ }
43
+ if (!sawWakePrefix && WAKE_PREFIX.test(line)) {
44
+ sawWakePrefix = true;
45
+ index += 1;
46
+ continue;
47
+ }
48
+ break;
49
+ }
50
+ const openLine = core(lines[index] ?? "").trim();
51
+ if (!openLine.startsWith(OPEN_PREFIX) || !openLine.endsWith(">"))
52
+ return null;
53
+ // タグ名の直後は属性区切りか終端のみ(<cross-session-messages 等の別タグを誤検知しない)。
54
+ const boundary = openLine.charAt(OPEN_PREFIX.length);
55
+ if (boundary !== " " && boundary !== ">")
56
+ return null;
57
+ const name = / from-name="([^"]*)"/u.exec(openLine)?.[1] ?? "";
58
+ const bodyLines = [];
59
+ for (let cursor = index + 1; cursor < lines.length; cursor += 1) {
60
+ if (core(lines[cursor] ?? "").trim() === CLOSER)
61
+ break;
62
+ bodyLines.push(lines[cursor] ?? "");
63
+ }
64
+ return { senderName: name === "" ? null : name, body: bodyLines.join("\n").trim() };
65
+ }
66
+ /** 表示用の送信元ラベル(名前が無い封筒のフォールバック込み)。 */
67
+ export function crossSessionSenderLabel(message) {
68
+ return message.senderName ?? "別セッション";
69
+ }
70
+ /** 一覧プレビュー/タイトル/検索スニペット向けの転写(「⇄ 送信元名: 本文」。本文なしは名前だけ)。 */
71
+ export function crossSessionPreviewLine(message) {
72
+ const label = `⇄ ${crossSessionSenderLabel(message)}`;
73
+ return message.body === "" ? label : `${label}: ${message.body}`;
74
+ }
75
+ //# sourceMappingURL=crossSession.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crossSession.js","sourceRoot":"","sources":["../../src/shared/crossSession.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * user 行へ注入される `<system-reminder>…</system-reminder>`(リマインダ・記憶リコール等)を
3
+ * 本文から除去する(閉じタグまでの最短一致。閉じが無い言及はそのまま残す)。
4
+ */
5
+ export declare function stripReminderTagBlocks(text: string): string;
6
+ /**
7
+ * assistant text へ行頭形で追記される harness 注入ブロックを除去する(停止境界の背景通知注入、
8
+ * Claude Code 2.1.251〜)。iOS `ChatLogModel.removingInjectedReminderBlocks` と同じ規則。
9
+ *
10
+ * 書式は `\n\n<system-reminder>\n…\n</system-reminder>\n`(同じ text ブロックに複数回現れ、
11
+ * 間にモデルの続きの本文が挟まる)。行単位で走査し:
12
+ * - 開始 = 行全体が `<system-reminder>` でその後に行が続く(行頭 + 直後改行)行。fence
13
+ * の内側は対象外(モデルが書式を例示したコードブロックを壊さない)。fence は CommonMark
14
+ * 規則: 先頭 3 空白まで + 3 つ以上の ` または ~ で開き、同じ記号が開き以上の長さで
15
+ * 続くだけの行で閉じる(4 連バッククォート内の ``` や ~~~ で閉じ誤らない)。
16
+ * バッククォート内や文中の言及は行頭でないので反応しない。
17
+ * - 終了 = 行全体が `</…reminder>` の行。内側に同じ開始行があれば入れ子として数える
18
+ * (`<result>` に書式が引用されても閉じタグを取り違えない)。閉じが無ければ末尾まで
19
+ * (実測の不一致/欠落は末尾のみ)。
20
+ * - ブロック前後の空行はまとめて落とし、前後に本文が残る場合だけ空行 1 つで区切る。
21
+ * それ以外の行(コードブロック内の空行等)は一切触らない。
22
+ * - 各行の比較(開き/閉じ/fence/空行)は行末の `\r` を除いた本体で行い、出力には元の行を残す
23
+ * (CRLF 混在や Monitor が中継した `\r` 付き出力でも開き/閉じを見失わない)。
24
+ */
25
+ export declare function stripInjectedReminderBlocks(text: string): string;
26
+ /**
27
+ * 注入ブロックの中身(開き/閉じ行を除く)を出現順に返す(無ければ空配列)。
28
+ * subagentTailer が「停止境界の背景通知注入」で届く `<task-notification>` を完了信号として
29
+ * 読むために使う。開始判定は strip と同じ門番(行頭 + 直後改行・fence 外・入れ子追跡)なので、
30
+ * モデルが本文や fence 内で引用した通知には反応しない。
31
+ */
32
+ export declare function injectedReminderBodies(text: string): string[];
@@ -0,0 +1,124 @@
1
+ // harness(Claude Code)が transcript へ注入する <system-reminder> ブロックの除去。
2
+ // 会話一覧プレビュー(claudeSessionStore)・会話検索スニペット(sessionSearch)・
3
+ // サブエージェント transcript ビューア(subagentTranscript)で共有し、subagentTailer は
4
+ // injectedReminderBodies で注入ブロック内の完了通知を読む(表示以外の唯一の利用者)。会話画面の
5
+ // 転写は iOS 側 ChatLogModel.present() が行い、assistant 形(stripInjectedReminderBlocks)は
6
+ // 同じ規則。user 形は iOS(閉じ無しを末尾まで落とす)と違い、閉じタグまでの最短一致だけ。
7
+ const OPEN_TAG = "<system-reminder>";
8
+ /** 閉じ行。実測で `</security-reminder>` と閉じる不一致形があるため `</…reminder>` を許容する。 */
9
+ const CLOSER_LINE = /^<\/[a-z-]*reminder>$/u;
10
+ /**
11
+ * user 行へ注入される `<system-reminder>…</system-reminder>`(リマインダ・記憶リコール等)を
12
+ * 本文から除去する(閉じタグまでの最短一致。閉じが無い言及はそのまま残す)。
13
+ */
14
+ export function stripReminderTagBlocks(text) {
15
+ if (!text.includes(OPEN_TAG))
16
+ return text;
17
+ return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/gu, "");
18
+ }
19
+ /**
20
+ * assistant text へ行頭形で追記される harness 注入ブロックを除去する(停止境界の背景通知注入、
21
+ * Claude Code 2.1.251〜)。iOS `ChatLogModel.removingInjectedReminderBlocks` と同じ規則。
22
+ *
23
+ * 書式は `\n\n<system-reminder>\n…\n</system-reminder>\n`(同じ text ブロックに複数回現れ、
24
+ * 間にモデルの続きの本文が挟まる)。行単位で走査し:
25
+ * - 開始 = 行全体が `<system-reminder>` でその後に行が続く(行頭 + 直後改行)行。fence
26
+ * の内側は対象外(モデルが書式を例示したコードブロックを壊さない)。fence は CommonMark
27
+ * 規則: 先頭 3 空白まで + 3 つ以上の ` または ~ で開き、同じ記号が開き以上の長さで
28
+ * 続くだけの行で閉じる(4 連バッククォート内の ``` や ~~~ で閉じ誤らない)。
29
+ * バッククォート内や文中の言及は行頭でないので反応しない。
30
+ * - 終了 = 行全体が `</…reminder>` の行。内側に同じ開始行があれば入れ子として数える
31
+ * (`<result>` に書式が引用されても閉じタグを取り違えない)。閉じが無ければ末尾まで
32
+ * (実測の不一致/欠落は末尾のみ)。
33
+ * - ブロック前後の空行はまとめて落とし、前後に本文が残る場合だけ空行 1 つで区切る。
34
+ * それ以外の行(コードブロック内の空行等)は一切触らない。
35
+ * - 各行の比較(開き/閉じ/fence/空行)は行末の `\r` を除いた本体で行い、出力には元の行を残す
36
+ * (CRLF 混在や Monitor が中継した `\r` 付き出力でも開き/閉じを見失わない)。
37
+ */
38
+ export function stripInjectedReminderBlocks(text) {
39
+ return scanInjectedReminderBlocks(text)?.stripped ?? text;
40
+ }
41
+ /**
42
+ * 注入ブロックの中身(開き/閉じ行を除く)を出現順に返す(無ければ空配列)。
43
+ * subagentTailer が「停止境界の背景通知注入」で届く `<task-notification>` を完了信号として
44
+ * 読むために使う。開始判定は strip と同じ門番(行頭 + 直後改行・fence 外・入れ子追跡)なので、
45
+ * モデルが本文や fence 内で引用した通知には反応しない。
46
+ */
47
+ export function injectedReminderBodies(text) {
48
+ return scanInjectedReminderBlocks(text)?.bodies ?? [];
49
+ }
50
+ function scanInjectedReminderBlocks(text) {
51
+ if (!text.includes(`${OPEN_TAG}\n`) && !text.includes(`${OPEN_TAG}\r\n`))
52
+ return null;
53
+ const lines = text.split("\n");
54
+ const kept = [];
55
+ const bodies = [];
56
+ let body = [];
57
+ let openFence = null;
58
+ let depth = 0;
59
+ let afterBlock = false;
60
+ let removedAny = false;
61
+ for (let index = 0; index < lines.length; index += 1) {
62
+ const line = lines[index] ?? "";
63
+ const core = line.endsWith("\r") ? line.slice(0, -1) : line;
64
+ if (depth > 0) {
65
+ if (core === OPEN_TAG) {
66
+ depth += 1;
67
+ }
68
+ else if (CLOSER_LINE.test(core)) {
69
+ depth -= 1;
70
+ if (depth === 0) {
71
+ bodies.push(body.join("\n"));
72
+ body = [];
73
+ continue;
74
+ }
75
+ }
76
+ body.push(line);
77
+ continue;
78
+ }
79
+ if (openFence === null && core === OPEN_TAG && index + 1 < lines.length) {
80
+ depth = 1;
81
+ removedAny = true;
82
+ while (kept.length > 0 && isBlankLine(kept[kept.length - 1] ?? ""))
83
+ kept.pop();
84
+ afterBlock = true;
85
+ continue;
86
+ }
87
+ if (afterBlock) {
88
+ if (isBlankLine(core))
89
+ continue;
90
+ if (kept.length > 0)
91
+ kept.push("");
92
+ afterBlock = false;
93
+ }
94
+ const run = fenceRun(core);
95
+ if (openFence !== null) {
96
+ if (run !== null && run.marker === openFence.marker && run.length >= openFence.length && isBlankLine(run.rest)) {
97
+ openFence = null;
98
+ }
99
+ }
100
+ else if (run !== null) {
101
+ openFence = { marker: run.marker, length: run.length };
102
+ }
103
+ kept.push(line);
104
+ }
105
+ if (depth > 0)
106
+ bodies.push(body.join("\n"));
107
+ return removedAny ? { stripped: kept.join("\n"), bodies } : null;
108
+ }
109
+ function isBlankLine(line) {
110
+ return /^[ \t\r]*$/u.test(line);
111
+ }
112
+ /**
113
+ * Markdown の fence 記号列(先頭 3 空白まで + 3 つ以上の ` または ~)。無ければ null。
114
+ * `rest` は記号列の後ろ(開き行なら info string、閉じ行なら空白のみ)。
115
+ */
116
+ function fenceRun(line) {
117
+ // `.` は \r に一致しないため [\s\S] で「記号列の後ろ全部」を取る(iOS 側と同じ切り方)。
118
+ const match = /^ {0,3}(`{3,}|~{3,})([\s\S]*)$/u.exec(line);
119
+ if (match === null)
120
+ return null;
121
+ const run = match[1] ?? "";
122
+ return { marker: run.charAt(0), length: run.length, rest: match[2] ?? "" };
123
+ }
124
+ //# sourceMappingURL=harnessReminder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessReminder.js","sourceRoot":"","sources":["../../src/shared/harnessReminder.ts"],"names":[],"mappings":"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"}
@@ -6,4 +6,11 @@ export declare class LineWriter {
6
6
  constructor(out: Writable);
7
7
  /** message をエンコードし、末尾改行を付けて書き込む。ストリーム破棄済みは例外。 */
8
8
  write(message: ControlMessage): void;
9
+ /**
10
+ * message を書き込み、出力バッファが高水位を超えていたら drain まで待つ。
11
+ * ファイル原本の分割配信(file-download)のように多量の行を続けて書く送出元が、
12
+ * 回線より速く書いてメモリに全チャンクを積み上げないための背圧付き write。
13
+ * 待機中にストリームが閉じたら(close/error)解放され、次の書込で例外になる。
14
+ */
15
+ writeWithBackpressure(message: ControlMessage): Promise<void>;
9
16
  }
@@ -15,5 +15,31 @@ export class LineWriter {
15
15
  }
16
16
  this.out.write(encodeControlMessage(message) + "\n");
17
17
  }
18
+ /**
19
+ * message を書き込み、出力バッファが高水位を超えていたら drain まで待つ。
20
+ * ファイル原本の分割配信(file-download)のように多量の行を続けて書く送出元が、
21
+ * 回線より速く書いてメモリに全チャンクを積み上げないための背圧付き write。
22
+ * 待機中にストリームが閉じたら(close/error)解放され、次の書込で例外になる。
23
+ */
24
+ async writeWithBackpressure(message) {
25
+ if (this.out.destroyed || !this.out.writable) {
26
+ throw new Error("LineWriter: output stream is closed");
27
+ }
28
+ const ok = this.out.write(encodeControlMessage(message) + "\n");
29
+ if (ok)
30
+ return;
31
+ const out = this.out;
32
+ await new Promise((resolve) => {
33
+ const done = () => {
34
+ out.off("drain", done);
35
+ out.off("close", done);
36
+ out.off("error", done);
37
+ resolve();
38
+ };
39
+ out.once("drain", done);
40
+ out.once("close", done);
41
+ out.once("error", done);
42
+ });
43
+ }
18
44
  }
19
45
  //# sourceMappingURL=lineWriter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lineWriter.js","sourceRoot":"","sources":["../../src/shared/lineWriter.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,8DAA8D;AAC9D,4EAA4E;AAG5E,OAAO,EAAE,oBAAoB,EAAuB,MAAM,gBAAgB,CAAC;AAE3E,yDAAyD;AACzD,MAAM,OAAO,UAAU;IACQ,GAAG;IAAhC,YAA6B,GAAa;mBAAb,GAAG;IAAa,CAAC;IAE9C,iDAAiD;IACjD,KAAK,CAAC,OAAuB;QAC3B,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,oBAAoB,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACvD,CAAC;CACF"}
1
+ {"version":3,"file":"lineWriter.js","sourceRoot":"","sources":["../../src/shared/lineWriter.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,8DAA8D;AAC9D,4EAA4E;AAG5E,OAAO,EAAE,oBAAoB,EAAuB,MAAM,gBAAgB,CAAC;AAE3E,yDAAyD;AACzD,MAAM,OAAO,UAAU;IACQ,GAAG;IAAhC,YAA6B,GAAa;mBAAb,GAAG;IAAa,CAAC;IAE9C,iDAAiD;IACjD,KAAK,CAAC,OAAuB;QAC3B,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,oBAAoB,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACvD,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,qBAAqB,CAAC,OAAuB;QACjD,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,oBAAoB,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;QAChE,IAAI,EAAE;YAAE,OAAO;QACf,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACrB,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAClC,MAAM,IAAI,GAAG,GAAS,EAAE;gBACtB,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;gBACvB,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;gBACvB,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;gBACvB,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC;YACF,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACxB,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACxB,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,30 @@
1
+ /** 1 ストリーム分の連投抑止状態(tailer が TailState に保持し、行ごとに渡す)。 */
2
+ export interface SystemNoticeContext {
3
+ /** 直近に告知した api_error の識別(status + 本文)。同一なら再試行ごとに出さない。 */
4
+ lastApiErrorKey: string | null;
5
+ /** 直近に告知した汎用 warning/error の本文。同一文言の連投を抑える。 */
6
+ lastGenericKey: string | null;
7
+ /** 直近に fallback ブロックから告知した切替(`from->to`)。system 行での重複告知を抑える。 */
8
+ lastFallbackKey: string | null;
9
+ }
10
+ export declare function createSystemNoticeContext(): SystemNoticeContext;
11
+ /**
12
+ * Claude のモデル id を表示名へ(`claude-fable-5-1` → `Fable 5.1`、`claude-opus-5[1m]` →
13
+ * `Opus 5 (1M)`、`claude-haiku-4-5-20251001` → `Haiku 4.5`、`claude-sonnet-4-20250514` →
14
+ * `Sonnet 4`)。8 桁の日付接尾辞は版番号より先に剥がす(`-(\d+)` の貪欲一致で
15
+ * `Sonnet 4.20250514` になっていた, 2026-09-04 レビュー指摘)。未知の形はそのまま返す。
16
+ * iOS 側のカタログに依存せず、host だけで人間向け表記を作る最小変換。
17
+ */
18
+ export declare function claudeModelDisplayName(id: string): string;
19
+ /**
20
+ * assistant 行の content にある `fallback` ブロック(`{type:"fallback", from:{model}, to:{model}}`,
21
+ * claude 2.1.25x)を切替告知へ。切替の実時刻に書かれる最速の信号で、フォールバック先モデルの
22
+ * 最初のツール実行より前に位置するため、ツールカード群を分断しない。無ければ null。
23
+ */
24
+ export declare function fallbackBlockNotice(content: unknown, ctx?: SystemNoticeContext): string | null;
25
+ /**
26
+ * system 行 1 件を利用者向け注記へ転写する。表に出さない行は null。
27
+ * 返す文字列は iOS の system 行としてそのまま表示される(先頭の絵文字で種別を示す)。
28
+ * `ctx` を渡すと連投・重複を抑止する(省略時は行単体で判定)。
29
+ */
30
+ export declare function systemNoticeText(rec: Record<string, unknown>, ctx?: SystemNoticeContext): string | null;
@@ -0,0 +1,209 @@
1
+ // systemNotice.ts
2
+ // tailii (TS host) — transcript の system 行のうち、利用者に見せるべき通知を日本語の
3
+ // system 注記へ転写する純ロジック(TESTABLE, system-notice)。
4
+ //
5
+ // 背景(2026-09-04 実障害): Fable 5.1 のセーフガードが応答を退け、Claude Code が Opus 4.8 へ
6
+ // 自動フォールバックした。transcript には `system/model_refusal_fallback` として残るが tailer が
7
+ // 落としていたため、アプリでは「理由なくモデル表示が変わった」ように見えた。同型で握り潰して
8
+ // いた system 行を棚卸しし(14日分・全 subtype/level を集計)、利用者に意味のあるものだけを
9
+ // ここで一元的に転写する:
10
+ // - assistant 行の `fallback` ブロック(from/to): 切替の**実時刻**に出す最速の信号
11
+ // (system 行はフォールバック先モデルの最初の応答完了後に書かれ、数十秒〜分単位で遅れる)
12
+ // - model_refusal_fallback / model_consent_fallback(warning): モデルの自動切替
13
+ // (同じ from→to を fallback ブロックで既に告知済みなら重複させない)
14
+ // - api_error(error): 初回試行・エラー種別の変化・認証/権限/レート系・再試行上限で出す
15
+ // (同じエラーの再試行ごとの連投は出さない)
16
+ // - compact_boundary(info): 会話の圧縮(Claude 側の文脈が要約に置き換わった事実)
17
+ // - それ以外の level=warning/error で本文があるもの(例: informational「Remote Control
18
+ // disconnected — /login」): 汎用注記。新しい警告 subtype も自動で表に出る(future-proof)。
19
+ // ただし端末向けの文言(「run /remote-control to move it to this terminal」= CLI 実行中の会話を
20
+ // Tailii で開いた複製インスタンス並走時の行)は iPhone 上で意味が無いので除く。
21
+ // 派生・内部情報(turn_duration / stop_hook_summary / away_summary / notice 級 / info 級の
22
+ // 未知 subtype)は従来どおり流さない。bridge_status / local_command は tailer 側の既存分岐が担う。
23
+ //
24
+ // 連投抑止のため、呼び出し側(tailer の 1 ストリーム)は `SystemNoticeContext` を持ち回る。
25
+ // context 無しでも動く(状態に依存する抑止だけが効かない)。
26
+ export function createSystemNoticeContext() {
27
+ return { lastApiErrorKey: null, lastGenericKey: null, lastFallbackKey: null };
28
+ }
29
+ /** 生の本文を注記へ載せるときの上限(iOS 側は lineLimit 無しなので host で切る)。 */
30
+ const RAW_TEXT_LIMIT = 200;
31
+ /** api_error の詳細(formatted / message 1 行目)の上限。 */
32
+ const ERROR_DETAIL_LIMIT = 160;
33
+ /**
34
+ * Claude のモデル id を表示名へ(`claude-fable-5-1` → `Fable 5.1`、`claude-opus-5[1m]` →
35
+ * `Opus 5 (1M)`、`claude-haiku-4-5-20251001` → `Haiku 4.5`、`claude-sonnet-4-20250514` →
36
+ * `Sonnet 4`)。8 桁の日付接尾辞は版番号より先に剥がす(`-(\d+)` の貪欲一致で
37
+ * `Sonnet 4.20250514` になっていた, 2026-09-04 レビュー指摘)。未知の形はそのまま返す。
38
+ * iOS 側のカタログに依存せず、host だけで人間向け表記を作る最小変換。
39
+ */
40
+ export function claudeModelDisplayName(id) {
41
+ let core = id;
42
+ let suffix = "";
43
+ const bracket = /\[([^\]]+)\]$/.exec(core);
44
+ if (bracket) {
45
+ suffix = ` (${(bracket[1] ?? "").toUpperCase()})`;
46
+ core = core.slice(0, bracket.index);
47
+ }
48
+ core = core.replace(/-\d{8}$/, "");
49
+ const match = /^claude-([a-z]+)-(\d{1,2})(?:-(\d{1,2}))?$/.exec(core);
50
+ if (!match)
51
+ return id;
52
+ const family = match[1] ?? "";
53
+ const familyName = family.charAt(0).toUpperCase() + family.slice(1);
54
+ const version = match[3] ? `${match[2]}.${match[3]}` : `${match[2]}`;
55
+ return `${familyName} ${version}${suffix}`;
56
+ }
57
+ function str(rec, key) {
58
+ const value = rec[key];
59
+ return typeof value === "string" && value.length > 0 ? value : null;
60
+ }
61
+ function num(rec, key) {
62
+ const value = rec[key];
63
+ if (typeof value === "number" && Number.isFinite(value))
64
+ return value;
65
+ if (typeof value === "string" && /^\d+$/.test(value))
66
+ return Number(value);
67
+ return null;
68
+ }
69
+ function obj(value) {
70
+ return typeof value === "object" && value !== null && !Array.isArray(value)
71
+ ? value
72
+ : null;
73
+ }
74
+ /** 前後空白を除き、上限を超えたら「…」で切る。改行は 1 スペースへ畳む(system 行は 1 段落)。 */
75
+ function clip(text, limit) {
76
+ const flat = text.replace(/\s*\n\s*/g, " ").trim();
77
+ if ([...flat].length <= limit)
78
+ return flat;
79
+ return [...flat].slice(0, limit).join("") + "…";
80
+ }
81
+ /** 端末向けで iPhone 上では意味の無い汎用警告(複製インスタンス並走時の RC 案内など)。 */
82
+ const TERMINAL_ONLY_WARNING = /Remote Control not started here/;
83
+ /** 再試行の途中でも必ず告知するべき HTTP ステータス(認証切れ / 権限 / レート制限)。 */
84
+ const NOTABLE_API_STATUSES = new Set([401, 403, 429]);
85
+ /** apiRefusalCategory はそのまま埋め込むため、想定外の値(長文・記号)を載せない。 */
86
+ const REFUSAL_CATEGORY = /^[a-z0-9_-]{1,32}$/;
87
+ function modelSwitchKey(from, to) {
88
+ return `${from ?? "?"}->${to}`;
89
+ }
90
+ /**
91
+ * assistant 行の content にある `fallback` ブロック(`{type:"fallback", from:{model}, to:{model}}`,
92
+ * claude 2.1.25x)を切替告知へ。切替の実時刻に書かれる最速の信号で、フォールバック先モデルの
93
+ * 最初のツール実行より前に位置するため、ツールカード群を分断しない。無ければ null。
94
+ */
95
+ export function fallbackBlockNotice(content, ctx) {
96
+ if (!Array.isArray(content))
97
+ return null;
98
+ for (const block of content) {
99
+ const rec = obj(block);
100
+ if (rec === null || rec["type"] !== "fallback")
101
+ continue;
102
+ const to = obj(rec["to"]) !== null ? str(obj(rec["to"]), "model") : null;
103
+ if (to === null)
104
+ continue;
105
+ const from = obj(rec["from"]) !== null ? str(obj(rec["from"]), "model") : null;
106
+ if (ctx)
107
+ ctx.lastFallbackKey = modelSwitchKey(from, to);
108
+ const fromName = from !== null ? claudeModelDisplayName(from) : "元のモデル";
109
+ const toName = claudeModelDisplayName(to);
110
+ return (`⚠️ ${fromName} が応答を退けたため、${toName} へ切り替えました。` +
111
+ `以降この会話は ${toName} で応答します。/model で変更できます。`);
112
+ }
113
+ return null;
114
+ }
115
+ /**
116
+ * system 行 1 件を利用者向け注記へ転写する。表に出さない行は null。
117
+ * 返す文字列は iOS の system 行としてそのまま表示される(先頭の絵文字で種別を示す)。
118
+ * `ctx` を渡すと連投・重複を抑止する(省略時は行単体で判定)。
119
+ */
120
+ export function systemNoticeText(rec, ctx) {
121
+ const subtype = str(rec, "subtype");
122
+ const level = str(rec, "level");
123
+ const content = (str(rec, "content") ?? "").trim();
124
+ // --- モデルの自動切替(セーフガード退け / 利用枠) ---
125
+ if (subtype === "model_refusal_fallback" || subtype === "model_consent_fallback") {
126
+ const to = str(rec, "fallbackModel");
127
+ if (to === null)
128
+ return content.length > 0 ? `⚠️ ${clip(content, RAW_TEXT_LIMIT)}` : null;
129
+ const from = str(rec, "originalModel");
130
+ const key = modelSwitchKey(from, to);
131
+ // 同じ切替を fallback ブロックで既に告知済み(実時刻)なら、遅れて来る system 行では出さない。
132
+ if (ctx && ctx.lastFallbackKey === key)
133
+ return null;
134
+ if (ctx)
135
+ ctx.lastFallbackKey = key;
136
+ const toName = claudeModelDisplayName(to);
137
+ const fromName = from !== null ? claudeModelDisplayName(from) : "元のモデル";
138
+ if (subtype === "model_refusal_fallback") {
139
+ const category = str(rec, "apiRefusalCategory");
140
+ const reason = category !== null && REFUSAL_CATEGORY.test(category) ? `(判定: ${category})` : "";
141
+ return (`⚠️ セーフガードにより ${fromName} が応答を退けたため、${toName} へ切り替えました${reason}。` +
142
+ `以降この会話は ${toName} で応答します。/model で変更できます。`);
143
+ }
144
+ return (`⚠️ ${fromName} は利用クレジットが必要なため、この会話は ${toName} へ切り替わりました。` +
145
+ `/model で変更できます。`);
146
+ }
147
+ // --- API エラー ---
148
+ // 出す条件: 初回試行 / エラー種別が直前の告知と変わった / 認証・権限・レート系 / 再試行上限。
149
+ // 同じエラーの再試行ごとの連投は出さない(実データ: 502 ×1 → 401 ×n。旧実装は初回だけで
150
+ // 「認証切れ(要 /login)」が永遠に出なかった, 2026-09-04 レビュー指摘)。
151
+ if (subtype === "api_error") {
152
+ const error = obj(rec["error"]);
153
+ const status = error !== null ? num(error, "status") : null;
154
+ const formatted = error !== null ? str(error, "formatted") : null;
155
+ const message = error !== null ? str(error, "message") : null;
156
+ const detailSource = formatted ?? (status !== null ? `HTTP ${status}` : (message?.split("\n")[0] ?? null));
157
+ const detail = detailSource !== null ? clip(detailSource, ERROR_DETAIL_LIMIT) : "";
158
+ const attempt = num(rec, "retryAttempt");
159
+ const max = num(rec, "maxRetries");
160
+ const key = `${status ?? "?"}|${detail}`;
161
+ const first = attempt === null || attempt <= 1;
162
+ const changed = ctx ? ctx.lastApiErrorKey !== key : false;
163
+ const notable = status !== null && NOTABLE_API_STATUSES.has(status);
164
+ const exhausted = attempt !== null && max !== null && max > 1 && attempt >= max;
165
+ // context があれば「同じエラーの繰り返し」を抑える。無ければ初回 / 認証系 / 上限だけ。
166
+ const emit = ctx ? (first || changed || exhausted) : (first || notable || exhausted);
167
+ if (!emit)
168
+ return null;
169
+ if (ctx)
170
+ ctx.lastApiErrorKey = key;
171
+ const head = detail.length > 0 ? `❌ API エラー: ${detail}` : "❌ API エラーが発生しました";
172
+ if (exhausted)
173
+ return `${head}(再試行 ${attempt} 回で上限に達しました)`;
174
+ if (notable && status === 401)
175
+ return `${head}(認証が切れています。/login でログインし直してください)`;
176
+ const retry = max !== null
177
+ ? `(自動再試行 ${attempt ?? 1}/${max} 回目)`
178
+ : "(自動再試行中)";
179
+ return `${head}${retry}`;
180
+ }
181
+ // --- 会話の圧縮(Claude 側の文脈が要約に置き換わった事実を残す) ---
182
+ if (subtype === "compact_boundary") {
183
+ const meta = obj(rec["compactMetadata"]);
184
+ const pre = meta !== null ? num(meta, "preTokens") : null;
185
+ const post = meta !== null ? num(meta, "postTokens") : null;
186
+ const trigger = meta !== null ? str(meta, "trigger") : null;
187
+ const how = trigger === "auto" ? "自動で" : trigger === "manual" ? "/compact で" : "";
188
+ const tokens = pre !== null && post !== null
189
+ ? `(${Math.round(pre).toLocaleString("en-US")} → ${Math.round(post).toLocaleString("en-US")} tokens)`
190
+ : "";
191
+ // アプリは transcript を全再生するので圧縮前の本文もそのまま見える。置き換わったのは
192
+ // Claude 側の文脈(次の応答が参照する記憶)であることが伝わる表現にする。
193
+ return `🧹 会話を${how}圧縮しました${tokens}。Claude 側の文脈は要約に置き換わりました(アプリの表示はそのまま)。`;
194
+ }
195
+ // --- 汎用: warning / error 級で本文があるものは表に出す(新しい警告 subtype も自動対応) ---
196
+ if ((level === "warning" || level === "error") && content.length > 0) {
197
+ if (TERMINAL_ONLY_WARNING.test(content))
198
+ return null;
199
+ const body = clip(content, RAW_TEXT_LIMIT);
200
+ if (ctx) {
201
+ if (ctx.lastGenericKey === body)
202
+ return null;
203
+ ctx.lastGenericKey = body;
204
+ }
205
+ return `${level === "error" ? "❌" : "⚠️"} ${body}`;
206
+ }
207
+ return null;
208
+ }
209
+ //# sourceMappingURL=systemNotice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"systemNotice.js","sourceRoot":"","sources":["../../src/shared/systemNotice.ts"],"names":[],"mappings":"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"}