cc-viewer 1.6.348 → 1.7.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.
- package/README.md +13 -1
- package/cli.js +116 -37
- package/dist/assets/App-gUSmU6Ny.js +2 -0
- package/dist/assets/{MdxEditorPanel-ualoRFOL.js → MdxEditorPanel-DAR39A3S.js} +1 -1
- package/dist/assets/Mobile-qfRsRth0.js +1 -0
- package/dist/assets/{_baseUniq-CG81mgN_.js → _baseUniq-D9_E5ZDp.js} +1 -1
- package/dist/assets/{arc-C161J4f2.js → arc-D4iFrlhb.js} +1 -1
- package/dist/assets/{architectureDiagram-Q4EWVU46-BmbDMdn9.js → architectureDiagram-Q4EWVU46-f1neo7hg.js} +1 -1
- package/dist/assets/{blockDiagram-DXYQGD6D-DXpMvdxd.js → blockDiagram-DXYQGD6D-DWi9e9tG.js} +1 -1
- package/dist/assets/{c4Diagram-AHTNJAMY-8Mypg0IU.js → c4Diagram-AHTNJAMY-DAdj49A2.js} +1 -1
- package/dist/assets/{channel-B2khdmvc.js → channel-B3VthT0m.js} +1 -1
- package/dist/assets/{chunk-4BX2VUAB-I61r1g6z.js → chunk-4BX2VUAB-BrPcAHV3.js} +1 -1
- package/dist/assets/{chunk-4TB4RGXK-VFPlRE5E.js → chunk-4TB4RGXK-Bvopc1V9.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-DvCamg_6.js → chunk-55IACEB6-Juw-7Z3i.js} +1 -1
- package/dist/assets/{chunk-EDXVE4YY-Dj4Q8RwR.js → chunk-EDXVE4YY-DVYP9tpB.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-_bgQuuJh.js → chunk-FMBD7UC4-VQ7VOXl3.js} +1 -1
- package/dist/assets/{chunk-OYMX7WX6-3to38LyN.js → chunk-OYMX7WX6-F8Rj4ZUZ.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-Zl26wKKz.js → chunk-QZHKN3VN-Dn7K3yi9.js} +1 -1
- package/dist/assets/{chunk-YZCP3GAM-RLYPrlh0.js → chunk-YZCP3GAM-UjO-KTsn.js} +1 -1
- package/dist/assets/classDiagram-6PBFFD2Q-DmSozxoO.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-DmSozxoO.js +1 -0
- package/dist/assets/clone-BQtvy9Ae.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-CsYOjJ0V.js → cose-bilkent-S5V4N54A-BDhNj8rq.js} +1 -1
- package/dist/assets/{dagre-KV5264BT-C3YXXaFU.js → dagre-KV5264BT-BzdcDuyt.js} +1 -1
- package/dist/assets/{diagram-5BDNPKRD-DUCOg1ld.js → diagram-5BDNPKRD-DP_SX65N.js} +1 -1
- package/dist/assets/{diagram-G4DWMVQ6-BtlLl_zX.js → diagram-G4DWMVQ6-B-pBFw8Q.js} +1 -1
- package/dist/assets/{diagram-MMDJMWI5-1Qp9xfOk.js → diagram-MMDJMWI5-6XSfN1eN.js} +1 -1
- package/dist/assets/{diagram-TYMM5635-Cj7VMp3L.js → diagram-TYMM5635-DJvGxvpE.js} +1 -1
- package/dist/assets/{erDiagram-SMLLAGMA-CVZVn541.js → erDiagram-SMLLAGMA-CpqdpGYj.js} +1 -1
- package/dist/assets/{flowDiagram-DWJPFMVM-CiU9Fkkv.js → flowDiagram-DWJPFMVM-C9m7wjwI.js} +1 -1
- package/dist/assets/{ganttDiagram-T4ZO3ILL-aRaG97ID.js → ganttDiagram-T4ZO3ILL-CU0XOfY3.js} +1 -1
- package/dist/assets/{gitGraphDiagram-UUTBAWPF-Cr2LJlvX.js → gitGraphDiagram-UUTBAWPF-ipyGULnP.js} +1 -1
- package/dist/assets/{graph-B59lDyMn.js → graph-BR2KmES9.js} +1 -1
- package/dist/assets/{index-C1ptnCpq.js → index--wgRW_OB.js} +1 -1
- package/dist/assets/index-B51Ci0GL.js +2 -0
- package/dist/assets/{index-CRAWA8PC.js → index-C-be29ey.js} +1 -1
- package/dist/assets/{index-CSM1Kw8l.js → index-C16fiVhv.js} +1 -1
- package/dist/assets/{index-Ddk-kg22.js → index-D5YruOKD.js} +1 -1
- package/dist/assets/{index-BnhNIjbj.js → index-D5tSeMU7.js} +1 -1
- package/dist/assets/{index-vFEQq-NB.js → index-DuZQ6FqR.js} +1 -1
- package/dist/assets/{index-CO2hZ1s4.js → index-OWzSX2T8.js} +1 -1
- package/dist/assets/{infoDiagram-42DDH7IO-CnO22bw8.js → infoDiagram-42DDH7IO-BQWQjfFX.js} +1 -1
- package/dist/assets/{ishikawaDiagram-UXIWVN3A-ByNzpScH.js → ishikawaDiagram-UXIWVN3A-Df-KH_Hz.js} +1 -1
- package/dist/assets/{journeyDiagram-VCZTEJTY-yaVaCWPq.js → journeyDiagram-VCZTEJTY-CMbB_4G9.js} +1 -1
- package/dist/assets/{jszip.min-BdVFP3OW.js → jszip.min-B6k2t9Ch.js} +1 -1
- package/dist/assets/{kanban-definition-6JOO6SKY-RIPR_RQ3.js → kanban-definition-6JOO6SKY-CImdlKN3.js} +1 -1
- package/dist/assets/{layout-0rzel0v_.js → layout-DQl4aw09.js} +1 -1
- package/dist/assets/{linear-sktN0tB6.js → linear-CLD3aPlK.js} +1 -1
- package/dist/assets/mermaid.core-CN35dvKJ.js +7 -0
- package/dist/assets/{min-rspGOkz_.js → min-_D86henJ.js} +1 -1
- package/dist/assets/{mindmap-definition-QFDTVHPH-BsVzZzTz.js → mindmap-definition-QFDTVHPH-2JVcu1nk.js} +1 -1
- package/dist/assets/{pieDiagram-DEJITSTG-CsoWsw4f.js → pieDiagram-DEJITSTG-Dik-m5u7.js} +1 -1
- package/dist/assets/{quadrantDiagram-34T5L4WZ-CTMKuCAI.js → quadrantDiagram-34T5L4WZ-Bm5l40Jq.js} +1 -1
- package/dist/assets/{requirementDiagram-MS252O5E-C67256te.js → requirementDiagram-MS252O5E-Ck9w5rTj.js} +1 -1
- package/dist/assets/{sankeyDiagram-XADWPNL6-X8Y7KnW0.js → sankeyDiagram-XADWPNL6-BbGVCFEf.js} +1 -1
- package/dist/assets/{seqResourceLoaders-B5Zy_CC_.css → seqResourceLoaders-DFrHmgnK.css} +1 -1
- package/dist/assets/seqResourceLoaders-DZJxMMaR.js +2 -0
- package/dist/assets/{sequenceDiagram-FGHM5R23-Cils0TAp.js → sequenceDiagram-FGHM5R23-vjvEdksk.js} +1 -1
- package/dist/assets/{stateDiagram-FHFEXIEX-DFnohvpG.js → stateDiagram-FHFEXIEX-DTqMoiB2.js} +1 -1
- package/dist/assets/{stateDiagram-v2-QKLJ7IA2-BTX_4o81.js → stateDiagram-v2-QKLJ7IA2-CNbAwnhu.js} +1 -1
- package/dist/assets/{timeline-definition-GMOUNBTQ-DrIYFrzZ.js → timeline-definition-GMOUNBTQ-Cw8O-52d.js} +1 -1
- package/dist/assets/{vendor-antd-D5HmSDSg.js → vendor-antd-DI7JL-mE.js} +1 -1
- package/dist/assets/{vendor-codemirror-C5yuj7ze.js → vendor-codemirror-B9c49dtM.js} +1 -1
- package/dist/assets/{vendor-mdxeditor-BcEooTvb.js → vendor-mdxeditor-B6cpBtIE.js} +2 -2
- package/dist/assets/{vendor-qrcode-d9LCn0-9.js → vendor-qrcode-Dn90-s-u.js} +1 -1
- package/dist/assets/{vendor-virtuoso-K9m4MN6a.js → vendor-virtuoso-CpdSKD_6.js} +1 -1
- package/dist/assets/{vennDiagram-DHZGUBPP-lRqBYTr9.js → vennDiagram-DHZGUBPP-DCuqymb7.js} +1 -1
- package/dist/assets/{wardley-RL74JXVD-e8YfxpOe.js → wardley-RL74JXVD-BJ20wJUU.js} +1 -1
- package/dist/assets/{wardleyDiagram-NUSXRM2D-Bxrg774h.js → wardleyDiagram-NUSXRM2D-Vz6US3qt.js} +1 -1
- package/dist/assets/{xychartDiagram-5P7HB3ND-DWhZqhq8.js → xychartDiagram-5P7HB3ND-CzkLKrmi.js} +1 -1
- package/dist/index.html +4 -4
- package/findcc.js +4 -3
- package/package.json +3 -1
- package/server/i18n.js +21 -141
- package/server/interceptor.js +211 -403
- package/server/lib/async-write-queue.js +12 -1
- package/server/lib/delta-reconstructor.js +3 -3
- package/server/lib/error-report.js +45 -0
- package/server/lib/im-log-watcher.js +68 -8
- package/server/lib/interceptor-core.js +19 -174
- package/server/lib/log-management.js +214 -247
- package/server/lib/log-stream.js +59 -108
- package/server/lib/log-watcher.js +62 -274
- package/server/lib/log-zip.js +148 -0
- package/server/lib/session-boundary.js +266 -0
- package/server/lib/session-pin-store.js +10 -19
- package/server/lib/stats-worker.js +138 -267
- package/server/lib/user-prompt-extract.js +250 -0
- package/server/lib/v2/adapter.js +1124 -0
- package/server/lib/v2/blob-store.js +44 -0
- package/server/lib/v2/conversation-store.js +217 -0
- package/server/lib/v2/convert-manager.js +79 -0
- package/server/lib/v2/convert-worker.js +42 -0
- package/server/lib/v2/convert.js +409 -0
- package/server/lib/v2/identity.js +204 -0
- package/server/lib/v2/journal.js +62 -0
- package/server/lib/v2/layout.js +167 -0
- package/server/lib/v2/live-feed.js +558 -0
- package/server/lib/v2/meta-rows.js +360 -0
- package/server/lib/v2/migrate-prompt.js +54 -0
- package/server/lib/v2/replay.js +191 -0
- package/server/lib/v2/session-select.js +222 -0
- package/server/lib/v2/singleflight.js +104 -0
- package/server/lib/v2/v2-writer.js +528 -0
- package/server/lib/v2/verify.js +233 -0
- package/server/lib/wire-compress.js +167 -0
- package/server/routes/events.js +120 -150
- package/server/routes/files-fs.js +4 -2
- package/server/routes/im.js +11 -2
- package/server/routes/logs.js +209 -164
- package/server/routes/preferences.js +0 -3
- package/server/routes/project-meta.js +2 -3
- package/server/routes/session-pin.js +7 -8
- package/server/routes/v2.js +60 -0
- package/server/routes/workspaces.js +50 -14
- package/server/server.js +70 -66
- package/server/workspace-registry.js +24 -4
- package/src/utils/apiUrl.js +26 -0
- package/src/utils/askFallback.js +21 -0
- package/src/utils/askOptionDesc.js +55 -0
- package/src/utils/askPortalMatcher.js +54 -0
- package/src/utils/autoApproveOptions.js +27 -0
- package/src/utils/avatarAnimationPostPass.js +49 -0
- package/src/utils/builtinPresets.js +18 -0
- package/src/utils/clearCheckpoint.js +7 -0
- package/src/utils/commandValidator.js +45 -0
- package/src/utils/contentFilter.js +620 -0
- package/src/utils/contextRaw.js +18 -0
- package/src/utils/contextSidebarNavigation.js +16 -0
- package/src/utils/contextTurns.js +117 -0
- package/src/utils/displayScaleHelper.js +47 -0
- package/src/utils/effectiveModel.js +13 -0
- package/src/utils/entry-slim.js +563 -0
- package/src/utils/entryCache.js +144 -0
- package/src/utils/errorReport.js +6 -0
- package/src/utils/fileExpandedPathsStorage.js +70 -0
- package/src/utils/fileIcons.jsx +36 -0
- package/src/utils/fileOpen.js +37 -0
- package/src/utils/formatters.js +48 -0
- package/src/utils/gitApi.js +50 -0
- package/src/utils/gitTreeBuilder.js +19 -0
- package/src/utils/helpers.js +846 -0
- package/src/utils/identityHeal.js +99 -0
- package/src/utils/imConnState.js +59 -0
- package/src/utils/imOrigin.js +24 -0
- package/src/utils/imTr.js +7 -0
- package/src/utils/imageCompress.js +83 -0
- package/src/utils/imageDownscale.js +28 -0
- package/src/utils/imageResize.js +112 -0
- package/src/utils/ingestPipeline.js +47 -0
- package/src/utils/lazyWithReload.js +53 -0
- package/src/utils/markdown.js +30 -0
- package/src/utils/markdownIncremental.js +39 -0
- package/src/utils/markdownProfiler.js +113 -0
- package/src/utils/mdExtensionDetect.js +64 -0
- package/src/utils/memoryLinkParser.js +44 -0
- package/src/utils/modalMask.js +15 -0
- package/src/utils/monotime.js +6 -0
- package/src/utils/pinnedMenu.js +55 -0
- package/src/utils/presetShortcuts.js +14 -0
- package/src/utils/projectAlias.js +135 -0
- package/src/utils/promptClassifier.js +112 -0
- package/src/utils/promptDetect.js +196 -0
- package/src/utils/promptNav.js +60 -0
- package/src/utils/ptyChunkBuilder.js +251 -0
- package/src/utils/quickMenuHoverIntent.js +35 -0
- package/src/utils/rateLimitParser.js +119 -0
- package/src/utils/readResultPool.js +106 -0
- package/src/utils/refreshCachedItemProp.js +30 -0
- package/src/utils/requestType.js +208 -0
- package/src/utils/resizeCalc.js +21 -0
- package/src/utils/resolveLocalized.js +17 -0
- package/src/utils/searchApi.js +47 -0
- package/src/utils/searchReplace.js +51 -0
- package/src/utils/seqResourceLoaders.js +79 -0
- package/src/utils/sessionManager.js +471 -0
- package/src/utils/sessionMerge.js +180 -0
- package/src/utils/skillModalController.js +111 -0
- package/src/utils/skillsParser.js +116 -0
- package/src/utils/slashCommandLabels.js +80 -0
- package/src/utils/splitDragCalc.js +73 -0
- package/src/utils/stickyBottomController.js +643 -0
- package/src/utils/svgSanitize.js +48 -0
- package/src/utils/systemTags.js +47 -0
- package/src/utils/tClaude.js +20 -0
- package/src/utils/teamModalBuilder.js +351 -0
- package/src/utils/teamSessionParser.js +190 -0
- package/src/utils/teammateAvatars.js +194 -0
- package/src/utils/teammateDetector.js +121 -0
- package/src/utils/termDiag.js +114 -0
- package/src/utils/terminalClipboard.js +117 -0
- package/src/utils/terminalWriteQueue.js +398 -0
- package/src/utils/thinkingMerge.js +49 -0
- package/src/utils/toolCatalog.js +29 -0
- package/src/utils/toolResultBuilder.js +393 -0
- package/src/utils/toolResultClassifier.js +16 -0
- package/src/utils/toolResultCore.js +167 -0
- package/src/utils/toolsDiff.js +44 -0
- package/src/utils/toolsXmlFormatter.js +3 -0
- package/src/utils/ultraplanController.js +109 -0
- package/src/utils/ultraplanExperts.js +86 -0
- package/src/utils/ultraplanTemplates.js +154 -0
- package/src/utils/userImageRefs.js +49 -0
- package/src/utils/v3Assembler.js +112 -0
- package/src/utils/v3Rows.js +48 -0
- package/src/utils/voicePackPlayer.js +233 -0
- package/src/utils/webSearchGrouping.js +111 -0
- package/src/utils/workflowFormat.js +52 -0
- package/src/utils/workflowRuns.js +118 -0
- package/src/utils/workflowStore.js +94 -0
- package/dist/assets/App-VTo4NVdo.js +0 -2
- package/dist/assets/Mobile-CcmYvMm_.js +0 -1
- package/dist/assets/classDiagram-6PBFFD2Q-BXnyUgsj.js +0 -1
- package/dist/assets/classDiagram-v2-HSJHXN6E-BXnyUgsj.js +0 -1
- package/dist/assets/clone-md-Mc1-T.js +0 -1
- package/dist/assets/index-DCo650PG.js +0 -2
- package/dist/assets/mermaid.core-C2Parj1q.js +0 -7
- package/dist/assets/seqResourceLoaders-BVozq68i.js +0 -2
- package/server/lib/instance-registry.js +0 -57
- package/server/lib/jsonl-archive.js +0 -180
- package/server/lib/teammate-detect.js +0 -32
- package/server/lib/zip-safety.js +0 -68
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// Shared user-prompt extraction — the server-side canonical for pulling clean,
|
|
2
|
+
// display-grade user prompts out of wire `messages` arrays and out of v2
|
|
3
|
+
// conversation event lines. Consumers: stats-worker previews, the V2Writer's
|
|
4
|
+
// per-session `prompts.jsonl` cache, the log-list read side, and the one-off
|
|
5
|
+
// backfill tooling. Keep them on THIS module — forked copies drift.
|
|
6
|
+
//
|
|
7
|
+
// NOTE: this is a deliberate server-side SUBSET of the frontend classifier.
|
|
8
|
+
// The full classification (synthetic prompts, secondary recycling, …) lives in
|
|
9
|
+
// src/utils/contentFilter.js:isSystemText — frontend (dist) and server are two
|
|
10
|
+
// separate bundles that cannot share a module, so only the rules that would
|
|
11
|
+
// LEAK into previews are mirrored here: system tags + cross-session teammate
|
|
12
|
+
// notifications.
|
|
13
|
+
import { openSync, readSync, closeSync } from 'node:fs';
|
|
14
|
+
|
|
15
|
+
// Cross-session / teammate protocol notification `type` whitelist (must match
|
|
16
|
+
// src/utils/contentFilter.js INTER_SESSION_NOTIFICATION_TYPES; add new types in
|
|
17
|
+
// BOTH places). One array derives the Set (brace scan) + the RegExp
|
|
18
|
+
// (isSystemText) so this file cannot drift internally.
|
|
19
|
+
// test/stats-worker-notification-filter.test.js guards frontend↔server sync.
|
|
20
|
+
export const INTER_SESSION_TYPES = [
|
|
21
|
+
'idle_notification', 'shutdown_request', 'shutdown_response', 'shutdown_approved',
|
|
22
|
+
'teammate_terminated', 'plan_approval_request', 'plan_approval_response',
|
|
23
|
+
];
|
|
24
|
+
const INTER_SESSION_TYPES_SET = new Set(INTER_SESSION_TYPES);
|
|
25
|
+
const INTER_SESSION_TYPES_RE = new RegExp(`"type"\\s*:\\s*"(?:${INTER_SESSION_TYPES.join('|')})"`);
|
|
26
|
+
|
|
27
|
+
// CLI-synthesized prompts (HTTP role=user but not typed by a human): Recap /
|
|
28
|
+
// Title / Compact / Topic / Summary. Must match src/utils/contentFilter.js
|
|
29
|
+
// SYNTHETIC_PROMPTS (KEEP IN SYNC — same rule set, `^`-anchored on trimmed
|
|
30
|
+
// text so quoted user text is never swallowed). Without this filter they leak
|
|
31
|
+
// into the "all user prompts" preview (observed on real data: "The user
|
|
32
|
+
// stepped away and is coming back. Recap in under 40 words…").
|
|
33
|
+
const SYNTHETIC_PROMPT_RES = [
|
|
34
|
+
/^The user stepped away and is coming back\. Recap in under/i,
|
|
35
|
+
/^(Based on the above conversation, generate a|Please write a)\s+(short|concise)\s+title/i,
|
|
36
|
+
/^(Your task is to create a detailed summary of the conversation|This session is being continued from a previous conversation)/i,
|
|
37
|
+
/^Analyze if this message indicates a new/i,
|
|
38
|
+
/^Summarize this coding session/i,
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
/** True when a text block is system-injected chrome, not a user prompt. */
|
|
42
|
+
export function isSystemText(text) {
|
|
43
|
+
if (!text) return true;
|
|
44
|
+
const trimmed = text.trim();
|
|
45
|
+
if (!trimmed) return true;
|
|
46
|
+
// Blocks that carry plan content must never be filtered (even when they
|
|
47
|
+
// open with a system tag).
|
|
48
|
+
if (/Implement the following plan:/i.test(trimmed)) return false;
|
|
49
|
+
if (/^<[a-zA-Z_][\w-]*[\s>]/i.test(trimmed)) return true; // incl. <teammate-message> wrappers
|
|
50
|
+
if (/^\[SUGGESTION MODE:/i.test(trimmed)) return true;
|
|
51
|
+
if (/^Your response was cut off because it exceeded the output token limit/i.test(trimmed)) return true;
|
|
52
|
+
if (/^Base directory for this skill:/i.test(trimmed)) return true;
|
|
53
|
+
for (const re of SYNTHETIC_PROMPT_RES) { if (re.test(trimmed)) return true; }
|
|
54
|
+
// Unwrapped cross-session teammate notifications: prefix line / newer caveat
|
|
55
|
+
// / bare protocol JSON — keep them out of previews.
|
|
56
|
+
if (/^Another Claude session sent a message:/i.test(trimmed)) return true;
|
|
57
|
+
if (/^This came from another Claude session\b/i.test(trimmed)) return true;
|
|
58
|
+
if (trimmed.startsWith('{') && INTER_SESSION_TYPES_RE.test(trimmed)) return true;
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Single-pass strip of top-level protocol-notification JSON objects (brace
|
|
63
|
+
// pairing handles nesting; braces inside string literals / escapes are
|
|
64
|
+
// skipped). Same semantics as contentFilter's scanTopLevelJsonObjects +
|
|
65
|
+
// extractProtocolNotifications — a flat `\{[^{}]*\}` regex cannot cross nested
|
|
66
|
+
// braces, so protocol bodies with nested fields (plan_approval_*) would leak.
|
|
67
|
+
function stripProtocolJson(s) {
|
|
68
|
+
if (typeof s !== 'string' || s.indexOf('{') === -1) return s;
|
|
69
|
+
let out = '', cursor = 0, depth = 0, start = -1, inStr = false, esc = false;
|
|
70
|
+
for (let i = 0; i < s.length; i++) {
|
|
71
|
+
const ch = s[i];
|
|
72
|
+
if (inStr) {
|
|
73
|
+
if (esc) esc = false;
|
|
74
|
+
else if (ch === '\\') esc = true;
|
|
75
|
+
else if (ch === '"') inStr = false;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (ch === '"') inStr = true;
|
|
79
|
+
else if (ch === '{') { if (depth === 0) start = i; depth++; }
|
|
80
|
+
else if (ch === '}' && depth > 0) {
|
|
81
|
+
depth--;
|
|
82
|
+
if (depth === 0 && start >= 0) {
|
|
83
|
+
let j; try { j = JSON.parse(s.slice(start, i + 1)); } catch { j = null; }
|
|
84
|
+
if (j && typeof j.type === 'string' && INTER_SESSION_TYPES_SET.has(j.type)) {
|
|
85
|
+
out += s.slice(cursor, start);
|
|
86
|
+
cursor = i + 1;
|
|
87
|
+
}
|
|
88
|
+
start = -1;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
out += s.slice(cursor);
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Strip system-injected tags, keeping the user text outside them (only for
|
|
98
|
+
* string-typed content where system chrome and user text can be mixed).
|
|
99
|
+
*/
|
|
100
|
+
export function stripSystemTags(text) {
|
|
101
|
+
let out = text
|
|
102
|
+
.replace(/<(system-reminder|local-command-caveat|project-reminder|important-instruction-reminders|file-modified-reminder|todo-reminder|user-prompt-submit-hook|local-command-stdout|command-name|task-notification|environment_details|context|teammate-message)\b[^>]*>[\s\S]*?<\/\1>/gi, '')
|
|
103
|
+
// Unwrapped cross-session notification chrome: prefix line + both caveat
|
|
104
|
+
// generations + bare protocol JSON (incl. nested) — strip, keep any user
|
|
105
|
+
// text mixed into the body.
|
|
106
|
+
.replace(/^Another Claude session sent a message:\s*/i, '')
|
|
107
|
+
.replace(/(^|\n)This came from another Claude session[\s\S]*?(?=\n\n|$)/i, '')
|
|
108
|
+
.replace(/(^|\n)IMPORTANT: This is NOT from your user[\s\S]*?(?=\n\n|$)/i, '');
|
|
109
|
+
out = stripProtocolJson(out);
|
|
110
|
+
return out.trim();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Extract the list of user prompt texts from a wire `messages` array.
|
|
115
|
+
* (Kept in sync with src/utils/contentFilter.js:classifyUserContent.)
|
|
116
|
+
*/
|
|
117
|
+
export function extractUserTexts(messages) {
|
|
118
|
+
const texts = [];
|
|
119
|
+
for (const msg of messages) {
|
|
120
|
+
if (msg.role !== 'user') continue;
|
|
121
|
+
if (typeof msg.content === 'string') {
|
|
122
|
+
// string content can mix system tags with user text — strip tags first
|
|
123
|
+
const text = stripSystemTags(msg.content).trim();
|
|
124
|
+
if (text && !isSystemText(text)) {
|
|
125
|
+
if (/^Implement the following plan:/i.test(text)) continue;
|
|
126
|
+
texts.push(text);
|
|
127
|
+
}
|
|
128
|
+
} else if (Array.isArray(msg.content)) {
|
|
129
|
+
const hasCommand = msg.content.some(b => b.type === 'text' && /<command-message>/i.test(b.text || ''));
|
|
130
|
+
const userParts = [];
|
|
131
|
+
for (const b of msg.content) {
|
|
132
|
+
if (b.type !== 'text') continue;
|
|
133
|
+
const text = (b.text || '').trim();
|
|
134
|
+
if (!text || isSystemText(text)) continue;
|
|
135
|
+
if (hasCommand && /<command-message>/i.test(text)) continue;
|
|
136
|
+
if (/^Implement the following plan:/i.test(text)) continue;
|
|
137
|
+
userParts.push(text);
|
|
138
|
+
}
|
|
139
|
+
if (userParts.length > 0) {
|
|
140
|
+
texts.push(userParts.join(' '));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return texts;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** True when a request is a SUGGESTION MODE probe (next-input prediction). */
|
|
148
|
+
export function isSuggestionMode(messages) {
|
|
149
|
+
if (!Array.isArray(messages) || messages.length === 0) return false;
|
|
150
|
+
const last = messages[messages.length - 1];
|
|
151
|
+
if (last?.role !== 'user') return false;
|
|
152
|
+
const content = last.content;
|
|
153
|
+
if (Array.isArray(content)) {
|
|
154
|
+
return content.some(b => b.type === 'text' && /^\[SUGGESTION MODE:/i.test((b.text || '').trim()));
|
|
155
|
+
}
|
|
156
|
+
if (typeof content === 'string') return /^\[SUGGESTION MODE:/im.test(content.trim());
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Preview normalization: one line, trimmed, capped at 100 chars (the stats
|
|
161
|
+
* precedent — a display truncation, not a prompt drop). */
|
|
162
|
+
export function flattenPromptText(text) {
|
|
163
|
+
return text.replace(/[\r\n]+/g, ' ').trim().slice(0, 100);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Numeric epoch-file sort: e2 before e10 (a lexical sort mis-orders them). */
|
|
167
|
+
export function sortEpochFiles(names) {
|
|
168
|
+
return names
|
|
169
|
+
.filter(f => /^e\d+\.jsonl$/.test(f))
|
|
170
|
+
.sort((a, b) => Number(a.match(/\d+/)[0]) - Number(b.match(/\d+/)[0]));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Fold conversation EVENT LINES into a deduped, display-ready prompt list.
|
|
175
|
+
* The single implementation shared by the stats preview, the read-side
|
|
176
|
+
* derivation and the backfill tooling:
|
|
177
|
+
* - snapshot/append lines carry `msgs:[…]`;
|
|
178
|
+
* - ctl replace-tail lines carry the swapped tail as `.msg` — a suggestion
|
|
179
|
+
* probe replaced by the REAL next prompt arrives exactly this way, so
|
|
180
|
+
* dropping ctl lines would lose those prompts;
|
|
181
|
+
* - skips whole SUGGESTION MODE events;
|
|
182
|
+
* - normalizes via flattenPromptText and dedupes via `seen`.
|
|
183
|
+
* @param {Iterable<object>} events parsed conversation event lines
|
|
184
|
+
* @param {{seen?: Set<string>, out?: string[]}} [acc] accumulator (reusable across epochs)
|
|
185
|
+
* @returns {string[]} the accumulated prompt list (=== acc.out when given)
|
|
186
|
+
*/
|
|
187
|
+
export function collectPromptsFromEvents(events, acc = {}) {
|
|
188
|
+
const seen = acc.seen || (acc.seen = new Set());
|
|
189
|
+
const out = acc.out || (acc.out = []);
|
|
190
|
+
for (const ev of events) {
|
|
191
|
+
if (!ev) continue;
|
|
192
|
+
let msgs = null;
|
|
193
|
+
if (Array.isArray(ev.msgs) && ev.msgs.length > 0) msgs = ev.msgs;
|
|
194
|
+
else if (ev.t === 'ctl' && ev.op === 'replace-tail' && ev.msg) msgs = [ev.msg];
|
|
195
|
+
if (!msgs) continue;
|
|
196
|
+
if (isSuggestionMode(msgs)) continue;
|
|
197
|
+
for (const text of extractUserTexts(msgs)) {
|
|
198
|
+
const flat = flattenPromptText(text);
|
|
199
|
+
if (flat && !seen.has(flat)) {
|
|
200
|
+
seen.add(flat);
|
|
201
|
+
out.push(flat);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return out;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Bounded head-read of a session's `prompts.jsonl`: parse at most `maxBytes`
|
|
210
|
+
* from the file head, one `{seq, texts:[…]}` object per line, dropping the
|
|
211
|
+
* (possibly truncated) tail beyond the budget. `readJsonlTolerant` reads whole
|
|
212
|
+
* files and must NOT be used here — prompts.jsonl of a resume-heavy long
|
|
213
|
+
* session is the one v2 file a list render touches per session, so the read
|
|
214
|
+
* must stay O(budget).
|
|
215
|
+
* @returns {string[]} deduped, display-ready prompts (empty when absent/unreadable)
|
|
216
|
+
*/
|
|
217
|
+
export function readPromptsHead(path, maxBytes = 256 * 1024) {
|
|
218
|
+
let fd;
|
|
219
|
+
let head = '';
|
|
220
|
+
try {
|
|
221
|
+
fd = openSync(path, 'r');
|
|
222
|
+
const buf = Buffer.alloc(maxBytes);
|
|
223
|
+
const n = readSync(fd, buf, 0, maxBytes, 0);
|
|
224
|
+
head = buf.toString('utf-8', 0, n);
|
|
225
|
+
} catch {
|
|
226
|
+
return [];
|
|
227
|
+
} finally {
|
|
228
|
+
if (fd !== undefined) { try { closeSync(fd); } catch { /* already closed */ } }
|
|
229
|
+
}
|
|
230
|
+
const seen = new Set();
|
|
231
|
+
const out = [];
|
|
232
|
+
const lines = head.split('\n');
|
|
233
|
+
// A read that filled the whole budget may end mid-line — drop that tail.
|
|
234
|
+
if (head.length >= maxBytes) lines.pop();
|
|
235
|
+
for (const line of lines) {
|
|
236
|
+
if (!line.trim()) continue;
|
|
237
|
+
let obj;
|
|
238
|
+
try { obj = JSON.parse(line); } catch { continue; } // torn/corrupt line — skip
|
|
239
|
+
if (!obj || !Array.isArray(obj.texts)) continue;
|
|
240
|
+
for (const t of obj.texts) {
|
|
241
|
+
if (typeof t !== 'string') continue;
|
|
242
|
+
const flat = flattenPromptText(t);
|
|
243
|
+
if (flat && !seen.has(flat)) {
|
|
244
|
+
seen.add(flat);
|
|
245
|
+
out.push(flat);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return out;
|
|
250
|
+
}
|