recallnest 1.4.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/.claude-plugin/CLAUDE.md +29 -0
- package/.claude-plugin/marketplace.json +18 -0
- package/.env.example +3 -0
- package/.github/workflows/ci.yml +28 -0
- package/.mcp.json +9 -0
- package/CHANGELOG.md +51 -0
- package/CLAUDE.md +42 -0
- package/DEMO.md +94 -0
- package/EVAL.md +89 -0
- package/FAILURES.md +75 -0
- package/LICENSE +21 -0
- package/OPERATIONS.md +59 -0
- package/README.md +427 -0
- package/README_CN.md +425 -0
- package/ROADMAP.md +198 -0
- package/assets/recallnest-workbench.png +0 -0
- package/assets/ui/app.js +807 -0
- package/assets/ui/index.html +154 -0
- package/assets/ui/styles.css +605 -0
- package/assets/wechat-qr.jpg +0 -0
- package/config.json.example +38 -0
- package/daily-recall.sh +87 -0
- package/daily-reflections/2026-03-05.md +15 -0
- package/daily-reflections/2026-03-06.md +15 -0
- package/daily-reflections/2026-03-07.md +7 -0
- package/daily-reflections/2026-03-08.md +34 -0
- package/daily-reflections/2026-03-09.md +35 -0
- package/daily-reflections/2026-03-10.md +36 -0
- package/daily-reflections/2026-03-11.md +35 -0
- package/daily-reflections/2026-03-12.md +36 -0
- package/daily-reflections/2026-03-13.md +35 -0
- package/daily-reflections/2026-03-14.md +35 -0
- package/daily-reflections/2026-03-15.md +35 -0
- package/daily-reflections/2026-03-16.md +41 -0
- package/daily-reflections/2026-03-17.md +10 -0
- package/daily-reflections/2026-03-18.md +10 -0
- package/daily-reflections/2026-03-19.md +10 -0
- package/daily-reflections/2026-03-20.md +10 -0
- package/daily-reflections/2026-03-21.md +10 -0
- package/daily-reflections/2026-03-22.md +10 -0
- package/daily-reflections/2026-03-23.md +10 -0
- package/daily-reflections/2026-03-24.md +10 -0
- package/daily-reflections/2026-03-25.md +32 -0
- package/daily-reflections/2026-03-26.md +37 -0
- package/daily-reflections/2026-03-27.md +35 -0
- package/daily-reflections/2026-03-28.md +35 -0
- package/daily-reflections/2026-03-29.md +35 -0
- package/daily-reflections/2026-03-30.md +36 -0
- package/daily-reflections/2026-03-31.md +35 -0
- package/daily-reflections/2026-04-01.md +35 -0
- package/docs/api-reference.md +1291 -0
- package/docs/architecture.md +168 -0
- package/docs/memory-boundary-contract.md +226 -0
- package/docs/memory-categories.md +119 -0
- package/docs/pipeline-audit-ultramemory.md +177 -0
- package/docs/plans/2026-03-08-query-expansion.md +270 -0
- package/docs/plans/2026-03-16-continuity-layer.md +196 -0
- package/docs/plans/mcp-tiering-plan.md +126 -0
- package/docs/research/recall-quality-audit-2026-03-22.md +153 -0
- package/docs/self-evolution.md +198 -0
- package/docs/superpowers/plans/2026-03-19-ingest-pipeline-upgrade.md +782 -0
- package/docs/superpowers/specs/2026-03-19-ingest-pipeline-upgrade-design.md +77 -0
- package/docs/superpowers/specs/2026-03-23-pull-claude-desktop-design.md +95 -0
- package/eval/cases.json +85 -0
- package/eval/continuity/case-seeds.json +90 -0
- package/eval/continuity/cases.json +302 -0
- package/eval/continuity/memory-seeds.json +26 -0
- package/eval/continuity/pattern-seeds.json +100 -0
- package/eval/continuity/results-2026-03-16.md +117 -0
- package/eval/continuity/results-2026-03-19.md +453 -0
- package/eval/continuity/runbook.md +110 -0
- package/eval/continuity/seed-cases.md +54 -0
- package/eval/reports/2026-03-06-abstract-query-baseline.md +96 -0
- package/eval/reports/2026-03-06-aws-ssh-baseline.md +82 -0
- package/eval/reports/2026-03-06-baseline.md +82 -0
- package/eval/reports/query-expansion.md +138 -0
- package/integrations/README.md +57 -0
- package/integrations/claude-code/README.md +133 -0
- package/integrations/claude-code/claude-md-snippet.md +31 -0
- package/integrations/claude-code/setup.sh +66 -0
- package/integrations/claude-code/smoke-continuity.sh +452 -0
- package/integrations/codex/README.md +49 -0
- package/integrations/codex/agents-md-snippet.md +31 -0
- package/integrations/codex/setup.sh +58 -0
- package/integrations/examples/claude-agent-sdk/memory-agent.ts +182 -0
- package/integrations/examples/langchain/memory-chain.py +111 -0
- package/integrations/examples/openai-agents-sdk/memory-agent.py +109 -0
- package/integrations/gemini-cli/README.md +57 -0
- package/integrations/gemini-cli/gemini-md-snippet.md +31 -0
- package/integrations/gemini-cli/setup.sh +64 -0
- package/integrations/shared/install-managed-block.sh +51 -0
- package/lm +19 -0
- package/package.json +50 -0
- package/plan-v1.2-distributable.md +105 -0
- package/scripts/archive-distilled-junk.ts +116 -0
- package/scripts/backfill-l0l1l2.ts +98 -0
- package/scripts/blind-scan.ts +588 -0
- package/scripts/cleanup-step1-exact-dups.ts +117 -0
- package/scripts/cleanup-step2-template-dups.ts +113 -0
- package/scripts/cleanup-step3-stale-content.ts +149 -0
- package/scripts/cleanup-step4-orphan-scopes.ts +131 -0
- package/scripts/delete-distilled-junk.ts +88 -0
- package/scripts/distill-facts.ts +305 -0
- package/scripts/health-check.ts +343 -0
- package/scripts/incremental-ingest.sh +71 -0
- package/scripts/mlp-health-check.ts +382 -0
- package/scripts/pull-claude-desktop.ts +515 -0
- package/scripts/re-ingest-cc.sh +66 -0
- package/scripts/rebalance-memory-health.ts +181 -0
- package/scripts/run-desktop-ingest.sh +11 -0
- package/scripts/start-server.sh +12 -0
- package/scripts/viz-dashboard.html +423 -0
- package/scripts/viz-export.ts +703 -0
- package/src/__tests__/access-tracker-novelty.test.ts +228 -0
- package/src/__tests__/auto-consolidation.test.ts +168 -0
- package/src/__tests__/auto-recall.test.ts +131 -0
- package/src/__tests__/batch-dedup.test.ts +65 -0
- package/src/__tests__/capture-engine.test.ts +1142 -0
- package/src/__tests__/capture-heuristic.test.ts +171 -0
- package/src/__tests__/cascade-forget.test.ts +210 -0
- package/src/__tests__/category-min-scores.test.ts +33 -0
- package/src/__tests__/confidence-tracker.test.ts +196 -0
- package/src/__tests__/conflict-advisor.test.ts +206 -0
- package/src/__tests__/conflict-escalation.test.ts +149 -0
- package/src/__tests__/conflict-lifecycle.test.ts +83 -0
- package/src/__tests__/conflict-output.test.ts +85 -0
- package/src/__tests__/conflict-store.test.ts +374 -0
- package/src/__tests__/consolidation-engine.test.ts +181 -0
- package/src/__tests__/context-composer-pins.test.ts +64 -0
- package/src/__tests__/context-composer-task-selection.test.ts +24 -0
- package/src/__tests__/context-renderer.test.ts +91 -0
- package/src/__tests__/continuity-eval.test.ts +275 -0
- package/src/__tests__/continuity-schema.test.ts +97 -0
- package/src/__tests__/core-recall-eval.test.ts +235 -0
- package/src/__tests__/core-summary.test.ts +109 -0
- package/src/__tests__/dedup-actions.test.ts +58 -0
- package/src/__tests__/deterministic-id.test.ts +39 -0
- package/src/__tests__/doctor.test.ts +303 -0
- package/src/__tests__/drill-down.test.ts +108 -0
- package/src/__tests__/embedder.test.ts +122 -0
- package/src/__tests__/entity-resolver.test.ts +58 -0
- package/src/__tests__/event-segmenter.test.ts +153 -0
- package/src/__tests__/hotness-blend.test.ts +76 -0
- package/src/__tests__/ingest.test.ts +360 -0
- package/src/__tests__/kg-extractor.test.ts +184 -0
- package/src/__tests__/kg-store.test.ts +198 -0
- package/src/__tests__/llm-consolidation.test.ts +226 -0
- package/src/__tests__/memory-boundaries.test.ts +237 -0
- package/src/__tests__/memory-health-rebalance.test.ts +144 -0
- package/src/__tests__/memory-output.test.ts +215 -0
- package/src/__tests__/multi-vector.test.ts +242 -0
- package/src/__tests__/ppr-traversal.test.ts +256 -0
- package/src/__tests__/preference-guard-smoke.test.ts +211 -0
- package/src/__tests__/preference-matcher.test.ts +230 -0
- package/src/__tests__/preference-slots.test.ts +106 -0
- package/src/__tests__/prospective-memory.test.ts +283 -0
- package/src/__tests__/query-entity-detector.test.ts +171 -0
- package/src/__tests__/query-expander.test.ts +34 -0
- package/src/__tests__/recall-mode.test.ts +64 -0
- package/src/__tests__/result-synthesizer.test.ts +198 -0
- package/src/__tests__/resume-context.test.ts +4743 -0
- package/src/__tests__/retrieval-trace.test.ts +213 -0
- package/src/__tests__/retriever-boundary-weight.test.ts +136 -0
- package/src/__tests__/retroactive-boost.test.ts +223 -0
- package/src/__tests__/scope-inventory.test.ts +200 -0
- package/src/__tests__/scope-policy.test.ts +69 -0
- package/src/__tests__/session-checkpoint.test.ts +259 -0
- package/src/__tests__/session-gc.test.ts +215 -0
- package/src/__tests__/store-scope-audit.test.ts +81 -0
- package/src/__tests__/temporal-parser.test.ts +241 -0
- package/src/__tests__/term-registry.test.ts +121 -0
- package/src/__tests__/tool-output-compressor.test.ts +133 -0
- package/src/__tests__/version-manager.test.ts +213 -0
- package/src/__tests__/workflow-observation.test.ts +400 -0
- package/src/access-tracker.ts +316 -0
- package/src/adaptive-retrieval.ts +64 -0
- package/src/api-server.ts +667 -0
- package/src/asset-sync.ts +79 -0
- package/src/auto-consolidation.ts +133 -0
- package/src/auto-gc.ts +124 -0
- package/src/auto-recall.ts +95 -0
- package/src/capture-engine.ts +931 -0
- package/src/capture-heuristic.ts +177 -0
- package/src/cascade-forget.ts +135 -0
- package/src/chunker.ts +253 -0
- package/src/cli.ts +1917 -0
- package/src/confidence-tracker.ts +166 -0
- package/src/conflict-advisor.ts +433 -0
- package/src/conflict-engine.ts +181 -0
- package/src/conflict-escalation.ts +138 -0
- package/src/conflict-lifecycle.ts +67 -0
- package/src/conflict-output.ts +218 -0
- package/src/conflict-schema.ts +130 -0
- package/src/conflict-store.ts +127 -0
- package/src/consolidation-engine.ts +256 -0
- package/src/context-composer-pins.ts +183 -0
- package/src/context-composer-queries.ts +121 -0
- package/src/context-composer-scope.ts +56 -0
- package/src/context-composer-stable-selection.ts +200 -0
- package/src/context-composer-stable.ts +217 -0
- package/src/context-composer-task-fallbacks.ts +36 -0
- package/src/context-composer-task-ranking.ts +197 -0
- package/src/context-composer-task-results.ts +137 -0
- package/src/context-composer-task-selection.ts +342 -0
- package/src/context-composer-text.ts +57 -0
- package/src/context-composer.ts +300 -0
- package/src/context-renderer.ts +179 -0
- package/src/decay-engine.ts +189 -0
- package/src/doctor.ts +543 -0
- package/src/embedder.ts +557 -0
- package/src/entity-resolver.ts +131 -0
- package/src/eval.ts +632 -0
- package/src/event-segmenter.ts +216 -0
- package/src/ingest.ts +1822 -0
- package/src/kg-extractor.ts +217 -0
- package/src/kg-store.ts +353 -0
- package/src/legacy-scope-review.ts +53 -0
- package/src/llm-client.ts +629 -0
- package/src/llm-consolidation.ts +168 -0
- package/src/mcp-server.ts +1337 -0
- package/src/memory-assets.ts +475 -0
- package/src/memory-boundaries.ts +354 -0
- package/src/memory-health-rebalance.ts +220 -0
- package/src/memory-output.ts +474 -0
- package/src/memory-schema.ts +184 -0
- package/src/multi-vector.ts +234 -0
- package/src/noise-filter.ts +155 -0
- package/src/ppr-traversal.ts +279 -0
- package/src/preference-matcher.ts +174 -0
- package/src/preference-slots.ts +259 -0
- package/src/prospective-memory.ts +198 -0
- package/src/query-entity-detector.ts +207 -0
- package/src/query-expander.ts +98 -0
- package/src/result-synthesizer.ts +114 -0
- package/src/retrieval-profiles.ts +101 -0
- package/src/retrieval-trace.ts +94 -0
- package/src/retriever.ts +1338 -0
- package/src/retroactive-boost.ts +154 -0
- package/src/rif.ts +73 -0
- package/src/runtime-config.ts +172 -0
- package/src/schema-utils.ts +56 -0
- package/src/scope-inventory.ts +310 -0
- package/src/scope-policy.ts +142 -0
- package/src/session-engine.ts +168 -0
- package/src/session-output.ts +96 -0
- package/src/session-schema.ts +60 -0
- package/src/session-store.ts +293 -0
- package/src/stderr-log.ts +16 -0
- package/src/store.ts +903 -0
- package/src/temporal-parser.ts +318 -0
- package/src/term-registry.ts +791 -0
- package/src/tool-output-compressor.ts +115 -0
- package/src/tracker.ts +57 -0
- package/src/ui-server.ts +430 -0
- package/src/version-manager.ts +172 -0
- package/src/workflow-observation-engine.ts +316 -0
- package/src/workflow-observation-managed.ts +83 -0
- package/src/workflow-observation-output.ts +86 -0
- package/src/workflow-observation-schema.ts +87 -0
- package/src/workflow-observation-scope-review.ts +409 -0
- package/src/workflow-observation-store.ts +69 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Segmentation (Tier 4.5)
|
|
3
|
+
*
|
|
4
|
+
* Replaces fixed-character chunking with semantic event boundaries for
|
|
5
|
+
* conversation text. Ensures retrieval gets complete semantic units
|
|
6
|
+
* (a full Q&A exchange, a complete list, an intact code block) rather
|
|
7
|
+
* than fragments cut mid-thought.
|
|
8
|
+
*
|
|
9
|
+
* Design: rule-based (zero LLM calls), CJK-aware, falls back to chunker
|
|
10
|
+
* for segments that exceed embedding model limits.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Types
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
export interface EventSegment {
|
|
18
|
+
text: string;
|
|
19
|
+
/** Reason this boundary was detected */
|
|
20
|
+
boundaryType: "topic_shift" | "qa_boundary" | "list_end" | "code_block" | "time_jump" | "initial" | "max_size";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface EventSegmenterConfig {
|
|
24
|
+
/** Max chars per segment before forced split (default: 2000) */
|
|
25
|
+
maxSegmentSize: number;
|
|
26
|
+
/** Min chars per segment to avoid fragments (default: 200) */
|
|
27
|
+
minSegmentSize: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const DEFAULT_EVENT_SEGMENTER_CONFIG: EventSegmenterConfig = {
|
|
31
|
+
maxSegmentSize: 2000,
|
|
32
|
+
minSegmentSize: 200,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// Boundary Detection Signals
|
|
37
|
+
// ============================================================================
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Topic shift markers — words/phrases that signal a new topic in conversation.
|
|
41
|
+
* Ordered roughly by strength of signal.
|
|
42
|
+
*/
|
|
43
|
+
const TOPIC_SHIFT_PATTERNS = [
|
|
44
|
+
// Strong signals (explicit topic change)
|
|
45
|
+
/^(?:\[用户\]|\[助手\])?\s*(?:另外|顺便|对了|换个话题|说到|关于另一个)/m,
|
|
46
|
+
/^(?:\[用户\]|\[助手\])?\s*(?:by the way|speaking of|on another note|moving on|anyway|also,? I wanted)/im,
|
|
47
|
+
// Medium signals (new question after discussion)
|
|
48
|
+
/^(?:\[用户\])\s*.{0,20}[??]\s*$/m,
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
/** Detect if text at a given line index starts a new topic */
|
|
52
|
+
function isTopicShift(line: string): boolean {
|
|
53
|
+
return TOPIC_SHIFT_PATTERNS.some(p => p.test(line));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Q&A boundary: a [用户] line after an [助手] block signals a new exchange.
|
|
58
|
+
*/
|
|
59
|
+
function isQABoundary(line: string, prevLine: string): boolean {
|
|
60
|
+
return /^\[用户\]/.test(line) && /^\[助手\]/.test(prevLine.trimStart());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Time jump: date/time markers that indicate a temporal discontinuity.
|
|
65
|
+
*/
|
|
66
|
+
const TIME_JUMP_PATTERN = /^(?:\[用户\]|\[助手\])?\s*(?:\d{4}[-/]\d{1,2}[-/]\d{1,2}|(?:今天|昨天|前天|上周|上个月|today|yesterday|last week))/i;
|
|
67
|
+
|
|
68
|
+
function isTimeJump(line: string): boolean {
|
|
69
|
+
return TIME_JUMP_PATTERN.test(line);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ============================================================================
|
|
73
|
+
// Code Block Tracking
|
|
74
|
+
// ============================================================================
|
|
75
|
+
|
|
76
|
+
/** Track code fence state to avoid splitting inside code blocks */
|
|
77
|
+
function isCodeFence(line: string): boolean {
|
|
78
|
+
return /^```/.test(line.trim());
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ============================================================================
|
|
82
|
+
// List Completeness
|
|
83
|
+
// ============================================================================
|
|
84
|
+
|
|
85
|
+
/** Detect numbered or bulleted list items */
|
|
86
|
+
const LIST_ITEM_PATTERN = /^\s*(?:\d+[.)]\s|[-*+]\s)/;
|
|
87
|
+
|
|
88
|
+
function isListItem(line: string): boolean {
|
|
89
|
+
return LIST_ITEM_PATTERN.test(line);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ============================================================================
|
|
93
|
+
// Core Segmenter
|
|
94
|
+
// ============================================================================
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Segment conversation text into semantic event units.
|
|
98
|
+
*
|
|
99
|
+
* Strategy:
|
|
100
|
+
* 1. Walk lines, tracking state (in-code-block, in-list, etc.)
|
|
101
|
+
* 2. At each potential boundary, decide: split or continue
|
|
102
|
+
* 3. Respect min/max size constraints
|
|
103
|
+
* 4. Never split inside code blocks or mid-list
|
|
104
|
+
*/
|
|
105
|
+
export function segmentEvents(
|
|
106
|
+
text: string,
|
|
107
|
+
config: EventSegmenterConfig = DEFAULT_EVENT_SEGMENTER_CONFIG,
|
|
108
|
+
): EventSegment[] {
|
|
109
|
+
if (!text || text.trim().length === 0) return [];
|
|
110
|
+
|
|
111
|
+
// Very short text: no point in segmenting
|
|
112
|
+
if (text.length <= config.minSegmentSize) {
|
|
113
|
+
return [{ text: text.trim(), boundaryType: "initial" }];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const lines = text.split("\n");
|
|
117
|
+
const segments: EventSegment[] = [];
|
|
118
|
+
let currentLines: string[] = [];
|
|
119
|
+
let currentLen = 0;
|
|
120
|
+
let inCodeBlock = false;
|
|
121
|
+
let inList = false;
|
|
122
|
+
let pendingBoundary: EventSegment["boundaryType"] | null = null;
|
|
123
|
+
|
|
124
|
+
function flushSegment(boundaryType: EventSegment["boundaryType"]) {
|
|
125
|
+
const segText = currentLines.join("\n").trim();
|
|
126
|
+
if (segText.length > 0) {
|
|
127
|
+
segments.push({ text: segText, boundaryType });
|
|
128
|
+
}
|
|
129
|
+
currentLines = [];
|
|
130
|
+
currentLen = 0;
|
|
131
|
+
pendingBoundary = null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
for (let i = 0; i < lines.length; i++) {
|
|
135
|
+
const line = lines[i];
|
|
136
|
+
const lineLen = line.length + 1; // +1 for \n
|
|
137
|
+
|
|
138
|
+
// Track code block state
|
|
139
|
+
if (isCodeFence(line)) {
|
|
140
|
+
inCodeBlock = !inCodeBlock;
|
|
141
|
+
currentLines.push(line);
|
|
142
|
+
currentLen += lineLen;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Never split inside code blocks
|
|
147
|
+
if (inCodeBlock) {
|
|
148
|
+
currentLines.push(line);
|
|
149
|
+
currentLen += lineLen;
|
|
150
|
+
// Safety: if code block is absurdly long, force close
|
|
151
|
+
if (currentLen > config.maxSegmentSize * 2) {
|
|
152
|
+
inCodeBlock = false;
|
|
153
|
+
flushSegment("code_block");
|
|
154
|
+
}
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Track list state
|
|
159
|
+
if (isListItem(line)) {
|
|
160
|
+
inList = true;
|
|
161
|
+
currentLines.push(line);
|
|
162
|
+
currentLen += lineLen;
|
|
163
|
+
continue;
|
|
164
|
+
} else if (inList && line.trim().length > 0) {
|
|
165
|
+
// Non-list line after list items: list ended
|
|
166
|
+
inList = false;
|
|
167
|
+
// If accumulated enough, this is a good split point
|
|
168
|
+
if (currentLen >= config.minSegmentSize) {
|
|
169
|
+
pendingBoundary = "list_end";
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Check boundary signals (only if we have enough content)
|
|
174
|
+
if (currentLen >= config.minSegmentSize) {
|
|
175
|
+
const prevLine = currentLines.length > 0 ? currentLines[currentLines.length - 1] : "";
|
|
176
|
+
|
|
177
|
+
if (isTopicShift(line)) {
|
|
178
|
+
flushSegment("topic_shift");
|
|
179
|
+
} else if (isQABoundary(line, prevLine)) {
|
|
180
|
+
flushSegment("qa_boundary");
|
|
181
|
+
} else if (isTimeJump(line)) {
|
|
182
|
+
flushSegment("time_jump");
|
|
183
|
+
} else if (pendingBoundary) {
|
|
184
|
+
flushSegment(pendingBoundary);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Force split if exceeding max size
|
|
189
|
+
if (currentLen + lineLen > config.maxSegmentSize && currentLen >= config.minSegmentSize) {
|
|
190
|
+
flushSegment("max_size");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
currentLines.push(line);
|
|
194
|
+
currentLen += lineLen;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Flush remaining
|
|
198
|
+
if (currentLines.length > 0) {
|
|
199
|
+
flushSegment(segments.length === 0 ? "initial" : "max_size");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Merge tiny trailing segments into the previous one
|
|
203
|
+
if (segments.length > 1) {
|
|
204
|
+
const last = segments[segments.length - 1];
|
|
205
|
+
if (last.text.length < config.minSegmentSize) {
|
|
206
|
+
const prev = segments[segments.length - 2];
|
|
207
|
+
segments[segments.length - 2] = {
|
|
208
|
+
text: prev.text + "\n" + last.text,
|
|
209
|
+
boundaryType: prev.boundaryType,
|
|
210
|
+
};
|
|
211
|
+
segments.pop();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return segments;
|
|
216
|
+
}
|