@poco-ai/agentero-dsh-host 0.0.0-stage → 0.1.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.
Files changed (64) hide show
  1. package/lib/bookmarks-write.d.ts +24 -0
  2. package/lib/bookmarks-write.js +181 -0
  3. package/lib/bookmarks-write.js.map +1 -0
  4. package/lib/catalog.d.ts +28 -0
  5. package/lib/catalog.js +58 -0
  6. package/lib/catalog.js.map +1 -0
  7. package/lib/cli-run.d.ts +15 -0
  8. package/lib/cli-run.js +102 -0
  9. package/lib/cli-run.js.map +1 -0
  10. package/lib/client/index.d.ts +71 -0
  11. package/lib/client/settings-model.d.ts +28 -0
  12. package/lib/client/settings.d.ts +27 -0
  13. package/lib/client/ui/button.d.ts +10 -0
  14. package/lib/client/ui/input.d.ts +2 -0
  15. package/lib/client/utils.d.ts +2 -0
  16. package/lib/client.js +4071 -0
  17. package/lib/index.d.ts +55 -0
  18. package/lib/index.js +1100 -0
  19. package/lib/index.js.map +1 -0
  20. package/lib/marks-store.d.ts +80 -0
  21. package/lib/marks-store.js +393 -0
  22. package/lib/marks-store.js.map +1 -0
  23. package/lib/notes-files.d.ts +47 -0
  24. package/lib/notes-files.js +427 -0
  25. package/lib/notes-files.js.map +1 -0
  26. package/lib/pdf-file.d.ts +23 -0
  27. package/lib/pdf-file.js +84 -0
  28. package/lib/pdf-file.js.map +1 -0
  29. package/lib/tools.d.ts +8 -0
  30. package/lib/tools.js +204 -0
  31. package/lib/tools.js.map +1 -0
  32. package/lib/translate.d.ts +70 -0
  33. package/lib/translate.js +80 -0
  34. package/lib/translate.js.map +1 -0
  35. package/lib/translation/batches.d.ts +21 -0
  36. package/lib/translation/batches.js +88 -0
  37. package/lib/translation/batches.js.map +1 -0
  38. package/lib/translation/chunks.d.ts +2 -0
  39. package/lib/translation/chunks.js +36 -0
  40. package/lib/translation/chunks.js.map +1 -0
  41. package/lib/translation/mask.d.ts +22 -0
  42. package/lib/translation/mask.js +69 -0
  43. package/lib/translation/mask.js.map +1 -0
  44. package/lib/translation/pipeline.d.ts +3 -0
  45. package/lib/translation/pipeline.js +119 -0
  46. package/lib/translation/pipeline.js.map +1 -0
  47. package/lib/translation/preserved-links.d.ts +26 -0
  48. package/lib/translation/preserved-links.js +127 -0
  49. package/lib/translation/preserved-links.js.map +1 -0
  50. package/lib/translation/prompt.d.ts +99 -0
  51. package/lib/translation/prompt.js +224 -0
  52. package/lib/translation/prompt.js.map +1 -0
  53. package/lib/translation/retry.d.ts +3 -0
  54. package/lib/translation/retry.js +30 -0
  55. package/lib/translation/retry.js.map +1 -0
  56. package/lib/translation/sentences.d.ts +5 -0
  57. package/lib/translation/sentences.js +77 -0
  58. package/lib/translation/sentences.js.map +1 -0
  59. package/lib/vault.d.ts +21 -0
  60. package/lib/vault.js +69 -0
  61. package/lib/vault.js.map +1 -0
  62. package/package.json +44 -3
  63. package/src/translation/MOTIF-LICENSE.txt +21 -0
  64. package/README.md +0 -3
package/lib/tools.js ADDED
@@ -0,0 +1,204 @@
1
+ import { discoverArxiv, importId } from './catalog.js';
2
+ import { cliFailure, runAgentero } from './cli-run.js';
3
+ import { listVaultNotes, readVaultFile } from './notes-files.js';
4
+ /** How many catalog rows a tool result may carry. */
5
+ const PAPER_CAP = 40;
6
+ /** How many note files a search may open. */
7
+ const SEARCH_CAP = 40;
8
+ /** How many search hits to return. */
9
+ const HIT_CAP = 20;
10
+ /** Note text returned to the model, in characters. */
11
+ const TEXT_CAP = 12_000;
12
+ const objectSchema = {
13
+ type: 'object',
14
+ additionalProperties: true,
15
+ };
16
+ /**
17
+ * Render a tool result. The tools API calls `render(arguments, value)`, so the
18
+ * value to serialize is the second parameter — declaring only one would
19
+ * stringify the arguments and hand the model an echo of its own call.
20
+ */
21
+ function textResult(_arguments, value) {
22
+ return [{ type: 'text', text: JSON.stringify(value) }];
23
+ }
24
+ function record(value) {
25
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
26
+ return undefined;
27
+ return value;
28
+ }
29
+ function stringArg(args, key) {
30
+ const row = record(args);
31
+ const value = row?.[key];
32
+ return typeof value === 'string' ? value.trim() : '';
33
+ }
34
+ async function paperList(settings, query, children) {
35
+ const args = [
36
+ '--vault', settings.vault,
37
+ '--json',
38
+ 'paper', 'list',
39
+ '--fields', 'year,authors,tags,date',
40
+ ];
41
+ if (query !== '')
42
+ args.push('--query', query);
43
+ const result = await runAgentero(settings.agenteroBin, args, 20_000, children);
44
+ if (result.timedOut || result.spawnError !== undefined || result.code !== 0) {
45
+ throw new Error(cliFailure(result, 'agentero paper list'));
46
+ }
47
+ const parsed = JSON.parse(result.stdout);
48
+ const data = record(parsed)?.data;
49
+ const rows = Array.isArray(data) ? data : [];
50
+ return {
51
+ papers: rows.slice(0, PAPER_CAP),
52
+ truncated: rows.length > PAPER_CAP,
53
+ };
54
+ }
55
+ /**
56
+ * Register model tools that read and import the same vault as the panels.
57
+ * @returns Disposer that unregisters every tool.
58
+ */
59
+ export function registerTools(ctx, settings, children) {
60
+ const tools = ctx.tools;
61
+ const dispose = [];
62
+ dispose.push(tools.register({
63
+ name: 'agentero_list_papers',
64
+ description: 'List papers already in the Agentero vault. Paths are vault-relative, such as papers/<id>. Do not invent a second catalog.',
65
+ parameters: {
66
+ type: 'object',
67
+ additionalProperties: false,
68
+ properties: {
69
+ query: { type: 'string', description: 'Optional substring matched by the Agentero CLI against title, authors, id, path, and tags.' },
70
+ },
71
+ },
72
+ output: { schema: { ...objectSchema }, render: textResult },
73
+ async execute(args) {
74
+ if (settings.vault === '')
75
+ throw new Error('vault is not configured');
76
+ return paperList(settings, stringArg(args, 'query'), children);
77
+ },
78
+ }));
79
+ dispose.push(tools.register({
80
+ name: 'agentero_import_paper',
81
+ description: 'Import one paper into the Agentero vault by arXiv id, DOI, or URL. Dedupes against papers already in the vault.',
82
+ parameters: {
83
+ type: 'object',
84
+ additionalProperties: false,
85
+ required: ['text'],
86
+ properties: {
87
+ text: { type: 'string', description: 'arXiv id, DOI, or paper URL.' },
88
+ },
89
+ },
90
+ output: { schema: { ...objectSchema }, render: textResult },
91
+ async execute(args) {
92
+ if (settings.vault === '')
93
+ throw new Error('vault is not configured');
94
+ const text = stringArg(args, 'text');
95
+ if (text === '')
96
+ throw new Error('text is required');
97
+ const result = await importId(settings, text, children);
98
+ if (result.status !== 200)
99
+ throw new Error(result.error);
100
+ return JSON.parse(result.raw);
101
+ },
102
+ }));
103
+ dispose.push(tools.register({
104
+ name: 'agentero_read_note',
105
+ description: 'Read one Markdown file inside the vault. Use notes/<file>.md for the notes library, or papers/<id>/NOTES.md for the note beside a paper. Paths are vault-relative. This does not return PDF page text.',
106
+ parameters: {
107
+ type: 'object',
108
+ additionalProperties: false,
109
+ required: ['path'],
110
+ properties: {
111
+ path: { type: 'string', description: 'Vault-relative path using / separators.' },
112
+ },
113
+ },
114
+ output: { schema: { ...objectSchema }, render: textResult },
115
+ async execute(args) {
116
+ if (settings.vault === '')
117
+ throw new Error('vault is not configured');
118
+ const path = stringArg(args, 'path');
119
+ if (path === '')
120
+ throw new Error('path is required');
121
+ const file = await readVaultFile(settings.vault, path);
122
+ if (file.status !== 200)
123
+ throw new Error(file.error);
124
+ const truncated = file.text.length > TEXT_CAP;
125
+ return {
126
+ path: file.path,
127
+ text: truncated ? file.text.slice(0, TEXT_CAP) : file.text,
128
+ truncated,
129
+ };
130
+ },
131
+ }));
132
+ dispose.push(tools.register({
133
+ name: 'agentero_search_notes',
134
+ description: 'Search Markdown files under the vault notes/ directory by title or body. Does not list per-paper NOTES.md files.',
135
+ parameters: {
136
+ type: 'object',
137
+ additionalProperties: false,
138
+ required: ['query'],
139
+ properties: {
140
+ query: { type: 'string', description: 'Substring matched against the note title, path, and body.' },
141
+ },
142
+ },
143
+ output: { schema: { ...objectSchema }, render: textResult },
144
+ async execute(args) {
145
+ if (settings.vault === '')
146
+ throw new Error('vault is not configured');
147
+ const query = stringArg(args, 'query').toLowerCase();
148
+ if (query === '')
149
+ throw new Error('query is required');
150
+ const notes = await listVaultNotes(settings.vault);
151
+ const hits = [];
152
+ let scanned = 0;
153
+ for (const note of notes) {
154
+ if (hits.length >= HIT_CAP || scanned >= SEARCH_CAP)
155
+ break;
156
+ const titleHit = note.title.toLowerCase().includes(query) || note.path.toLowerCase().includes(query);
157
+ scanned += 1;
158
+ const file = await readVaultFile(settings.vault, note.path);
159
+ if (file.status !== 200)
160
+ continue;
161
+ const body = file.text;
162
+ const at = body.toLowerCase().indexOf(query);
163
+ if (!titleHit && at < 0)
164
+ continue;
165
+ const start = at < 0 ? 0 : Math.max(0, at - 80);
166
+ hits.push({
167
+ path: note.path,
168
+ title: note.title,
169
+ excerpt: body.slice(start, start + 240),
170
+ });
171
+ }
172
+ return { notes: hits };
173
+ },
174
+ }));
175
+ dispose.push(tools.register({
176
+ name: 'agentero_discover_arxiv',
177
+ description: 'Rank a short arXiv list for one keyword. Papers already in the vault are omitted. Import a chosen id with agentero_import_paper.',
178
+ parameters: {
179
+ type: 'object',
180
+ additionalProperties: false,
181
+ required: ['keyword'],
182
+ properties: {
183
+ keyword: { type: 'string', description: 'Topic term or phrase.' },
184
+ },
185
+ },
186
+ output: { schema: { ...objectSchema }, render: textResult },
187
+ async execute(args) {
188
+ if (settings.vault === '')
189
+ throw new Error('vault is not configured');
190
+ const keyword = stringArg(args, 'keyword');
191
+ if (keyword === '')
192
+ throw new Error('keyword is required');
193
+ const result = await discoverArxiv(settings, keyword, children);
194
+ if (result.status !== 200)
195
+ throw new Error(result.error);
196
+ return JSON.parse(result.raw);
197
+ },
198
+ }));
199
+ return () => {
200
+ for (const remove of dispose)
201
+ remove();
202
+ };
203
+ }
204
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /** One-shot PDF translation through the Harness model registry. No session or tools are used. */
2
+ export interface TranslationSettings {
3
+ translationProvider: string;
4
+ translationModel: string;
5
+ translationSystemPrompt?: string;
6
+ translationUserPrompt?: string;
7
+ }
8
+ interface CallControls {
9
+ temperature?: number;
10
+ maxTokens?: number;
11
+ reasoningEffort?: string;
12
+ }
13
+ interface CallConfig extends CallControls {
14
+ provider: string;
15
+ model: string;
16
+ }
17
+ /** Minimal public dsh-llm seam, like the host's other injected service faces. */
18
+ export interface TranslationLlm {
19
+ prepareCall(config: CallConfig, signal: AbortSignal, configure: (controls: CallControls, model: {
20
+ reasoning?: {
21
+ efforts: readonly {
22
+ id: string;
23
+ }[];
24
+ };
25
+ }) => CallControls): Promise<{
26
+ config: CallConfig;
27
+ stream(options: CallConfig & {
28
+ system: string;
29
+ messages: {
30
+ role: 'user';
31
+ content: {
32
+ type: 'text';
33
+ text: string;
34
+ }[];
35
+ }[];
36
+ signal: AbortSignal;
37
+ }): AsyncIterable<{
38
+ type: string;
39
+ index?: number;
40
+ block?: {
41
+ type: string;
42
+ text?: string;
43
+ };
44
+ reason?: {
45
+ kind: string;
46
+ failure?: {
47
+ message: string;
48
+ status?: number;
49
+ statusCode?: number;
50
+ code?: string;
51
+ };
52
+ };
53
+ }>;
54
+ }>;
55
+ }
56
+ export interface DefaultTranslationModel {
57
+ currentSelection(): {
58
+ provider: string;
59
+ model: string;
60
+ };
61
+ }
62
+ export type TranslateText = (text: string, target: string, signal: AbortSignal) => Promise<string>;
63
+ export declare function translationTarget(value: unknown): string;
64
+ /** The prompt rules and customization semantics are shared with motif. */
65
+ export declare function translationMessages(text: string, target: string, settings: TranslationSettings): {
66
+ system: string;
67
+ user: string;
68
+ };
69
+ export declare function createTranslator(settings: TranslationSettings, getLlm: () => TranslationLlm | undefined, getDefault: () => DefaultTranslationModel | undefined): TranslateText;
70
+ export {};
@@ -0,0 +1,80 @@
1
+ /** One-shot PDF translation through the Harness model registry. No session or tools are used. */
2
+ import { retryTranslation } from './translation/retry.js';
3
+ import { buildTranslateMessages } from './translation/prompt.js';
4
+ import { translateDocumentText } from './translation/pipeline.js';
5
+ export function translationTarget(value) {
6
+ const target = typeof value === 'string' ? value.trim() : '';
7
+ return /^[a-zA-Z]{2,3}(?:-[a-zA-Z0-9]{2,8})?$/.test(target) ? target : 'zh-CN';
8
+ }
9
+ /** The prompt rules and customization semantics are shared with motif. */
10
+ export function translationMessages(text, target, settings) {
11
+ const language = /^zh(?:-CN)?$/i.test(target) ? 'Simplified Chinese'
12
+ : /^zh-(TW|HK)$/i.test(target) ? 'Traditional Chinese'
13
+ : /^en(?:-|$)/i.test(target) ? 'English' : target;
14
+ return buildTranslateMessages({ text, targetLangName: language,
15
+ customSystem: settings.translationSystemPrompt, customUser: settings.translationUserPrompt });
16
+ }
17
+ export function createTranslator(settings, getLlm, getDefault) {
18
+ return async (input, target, parentSignal) => {
19
+ const text = input.trim();
20
+ if (text === '' || text.length > 20_000)
21
+ throw new Error('Translation requires 1–20000 characters.');
22
+ const provider = settings.translationProvider.trim();
23
+ const model = settings.translationModel.trim();
24
+ if ((provider === '') !== (model === '')) {
25
+ throw new Error('Configure translationProvider and translationModel together in agentero-host.');
26
+ }
27
+ const route = provider === '' ? getDefault()?.currentSelection() : { provider, model };
28
+ if (route === undefined || route.provider === '' || route.model === '') {
29
+ throw new Error('Select a default model in Harness, or configure translationProvider and translationModel in agentero-host.');
30
+ }
31
+ const llm = getLlm();
32
+ if (llm === undefined)
33
+ throw new Error('Harness LLM service is unavailable. Enable a model provider for PDF translation.');
34
+ const operationSignal = AbortSignal.any([parentSignal, AbortSignal.timeout(300_000)]);
35
+ const translateChunk = (source) => retryTranslation(async () => {
36
+ const signal = AbortSignal.any([operationSignal, AbortSignal.timeout(90_000)]);
37
+ signal.throwIfAborted();
38
+ const call = await llm.prepareCall({ provider: route.provider, model: route.model, temperature: 0.2, maxTokens: 8192 }, signal, (controls, info) => {
39
+ // Effort IDs belong to the adapter. Its catalog orders them least to greatest.
40
+ const effort = info.reasoning?.efforts[0]?.id;
41
+ return { ...controls, ...(effort === undefined ? {} : { reasoningEffort: effort }) };
42
+ });
43
+ signal.throwIfAborted();
44
+ const messages = translationMessages(source, translationTarget(target), settings);
45
+ const blocks = new Map();
46
+ let complete = false;
47
+ for await (const chunk of call.stream({
48
+ ...call.config,
49
+ system: messages.system,
50
+ messages: [{ role: 'user', content: [{ type: 'text', text: messages.user }] }],
51
+ signal,
52
+ })) {
53
+ signal.throwIfAborted();
54
+ // block-end is authoritative; accumulating deltas as well would duplicate text.
55
+ if (chunk.type === 'block-end' && chunk.block?.type === 'text' && typeof chunk.block.text === 'string' && typeof chunk.index === 'number') {
56
+ blocks.set(chunk.index, chunk.block.text);
57
+ }
58
+ if (chunk.type === 'finish') {
59
+ if (chunk.reason?.kind !== 'stop') {
60
+ const error = new Error(chunk.reason?.failure?.message ?? `Translation incomplete (${chunk.reason?.kind ?? 'unknown'}). Try a smaller selection.`);
61
+ Object.assign(error, chunk.reason?.failure);
62
+ if (chunk.reason?.kind === 'max-tokens')
63
+ Object.assign(error, { code: 'TRANSLATION_INCOMPLETE' });
64
+ throw error;
65
+ }
66
+ complete = true;
67
+ }
68
+ }
69
+ signal.throwIfAborted();
70
+ if (!complete)
71
+ throw new Error('Translation stream ended before completion. Please retry.');
72
+ const result = [...blocks].sort(([a], [b]) => a - b).map(([, value]) => value).join('\n').trim();
73
+ if (result === '')
74
+ throw new Error('The model returned no translation. Please retry or select another model.');
75
+ return result;
76
+ }, operationSignal);
77
+ return translateDocumentText(text, translationTarget(target), translateChunk, operationSignal);
78
+ };
79
+ }
80
+ //# sourceMappingURL=translate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translate.js","sourceRoot":"","sources":["../src/translate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ export declare const TRANSLATE_BATCH_CHARS = 4500;
2
+ /** Anything batchable: a single region or a joined paragraph chain. */
3
+ type TranslateUnit = {
4
+ source: string;
5
+ };
6
+ /**
7
+ * Group units (already in reading order) into batches whose combined payload
8
+ * stays under {@link TRANSLATE_BATCH_CHARS}. Oversized units are kept
9
+ * alone and split by the transport. Batches may span adjacent pages; results are mapped
10
+ * back per unit, so positions are unaffected.
11
+ */
12
+ export declare function buildTranslateBatches<T extends TranslateUnit>(units: readonly T[]): T[][];
13
+ /** Join a batch into one numbered payload; a lone unit is sent as-is. */
14
+ export declare function buildNumberedPayload(batch: readonly TranslateUnit[]): string;
15
+ /**
16
+ * Split a numbered translation back into `expected` segments by `[[n]]` markers.
17
+ * Returns null when markers are missing/out of order or any segment is empty, so
18
+ * the caller can fall back to translating each sentence individually.
19
+ */
20
+ export declare function parseNumberedTranslation(result: string, expected: number): string[] | null;
21
+ export {};
@@ -0,0 +1,88 @@
1
+ // Ported from motif (Copyright (c) 2026 poco-ai, MIT). See MOTIF-LICENSE.txt.
2
+ export const TRANSLATE_BATCH_CHARS = 4500;
3
+ const TRANSLATE_BATCH_MARKER_RE = /(\[{2}|[{2})\s*(\d+)\s*(\]{2}|]{2})/g;
4
+ /** Projected payload length of a batch (matches {@link buildNumberedPayload}). */
5
+ function batchPayloadLength(batch) {
6
+ if (batch.length === 0)
7
+ return 0;
8
+ if (batch.length === 1)
9
+ return batch[0]?.source.length ?? 0;
10
+ let total = 0;
11
+ batch.forEach((unit, i) => {
12
+ total += `[[${i + 1}]] `.length + unit.source.length;
13
+ if (i > 0)
14
+ total += 2; // "\n\n"
15
+ });
16
+ return total;
17
+ }
18
+ /**
19
+ * Group units (already in reading order) into batches whose combined payload
20
+ * stays under {@link TRANSLATE_BATCH_CHARS}. Oversized units are kept
21
+ * alone and split by the transport. Batches may span adjacent pages; results are mapped
22
+ * back per unit, so positions are unaffected.
23
+ */
24
+ export function buildTranslateBatches(units) {
25
+ const batches = [];
26
+ let current = [];
27
+ for (const unit of units) {
28
+ const candidate = [...current, unit];
29
+ if (current.length > 0 &&
30
+ batchPayloadLength(candidate) > TRANSLATE_BATCH_CHARS) {
31
+ batches.push(current);
32
+ current = [unit];
33
+ }
34
+ else {
35
+ current = candidate;
36
+ }
37
+ }
38
+ if (current.length > 0)
39
+ batches.push(current);
40
+ return batches;
41
+ }
42
+ /** Join a batch into one numbered payload; a lone unit is sent as-is. */
43
+ export function buildNumberedPayload(batch) {
44
+ if (batch.length === 1)
45
+ return batch[0]?.source ?? "";
46
+ return batch.map((unit, i) => `[[${i + 1}]] ${unit.source}`).join("\n\n");
47
+ }
48
+ /**
49
+ * Split a numbered translation back into `expected` segments by `[[n]]` markers.
50
+ * Returns null when markers are missing/out of order or any segment is empty, so
51
+ * the caller can fall back to translating each sentence individually.
52
+ */
53
+ export function parseNumberedTranslation(result, expected) {
54
+ const trimmed = result.trim();
55
+ if (expected <= 1)
56
+ return trimmed ? [trimmed] : null;
57
+ const markers = [];
58
+ const re = new RegExp(TRANSLATE_BATCH_MARKER_RE.source, "g");
59
+ let m = re.exec(trimmed);
60
+ while (m !== null) {
61
+ markers.push({
62
+ n: Number(m[2]),
63
+ start: m.index,
64
+ end: m.index + m[0].length,
65
+ });
66
+ m = re.exec(trimmed);
67
+ }
68
+ if (markers.length !== expected || trimmed.slice(0, markers[0]?.start).trim())
69
+ return null;
70
+ const chosen = markers.slice(0, expected);
71
+ for (let i = 0; i < expected; i++) {
72
+ if (chosen[i]?.n !== i + 1)
73
+ return null;
74
+ }
75
+ const segments = [];
76
+ for (let i = 0; i < expected; i++) {
77
+ const cur = chosen[i];
78
+ if (!cur)
79
+ return null;
80
+ const nextStart = i + 1 < expected ? chosen[i + 1]?.start : trimmed.length;
81
+ const seg = trimmed.slice(cur.end, nextStart ?? trimmed.length).trim();
82
+ if (!seg)
83
+ return null;
84
+ segments.push(seg);
85
+ }
86
+ return segments;
87
+ }
88
+ //# sourceMappingURL=batches.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batches.js","sourceRoot":"","sources":["../../src/translation/batches.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ /** Lossless transport splitting. Geometry and sentence anchors stay upstream. */
2
+ export declare function splitTranslationChunks(text: string, limit: number): string[];
@@ -0,0 +1,36 @@
1
+ // Ported from motif (Copyright (c) 2026 poco-ai, MIT). See MOTIF-LICENSE.txt.
2
+ /** Lossless transport splitting. Geometry and sentence anchors stay upstream. */
3
+ export function splitTranslationChunks(text, limit) {
4
+ if (!Number.isInteger(limit) || limit < 32)
5
+ throw new Error("Invalid chunk limit");
6
+ const chunks = [];
7
+ let start = 0;
8
+ while (start < text.length) {
9
+ let end = Math.min(text.length, start + limit);
10
+ if (end < text.length) {
11
+ // Prefer a word/clause boundary in the latter half of the budget.
12
+ const floor = start + Math.floor(limit / 2);
13
+ for (let i = end; i > floor; i--) {
14
+ if (/[\s,。;!?]/u.test(text[i - 1] ?? "")) {
15
+ end = i;
16
+ break;
17
+ }
18
+ }
19
+ // Never tear an opaque placeholder or UTF-16 surrogate pair apart.
20
+ const open = text.lastIndexOf("⟦", end - 1);
21
+ const close = text.indexOf("⟧", open);
22
+ if (open >= start && open < end && close >= end) {
23
+ if (open === start)
24
+ throw new Error("Translation token exceeds request budget");
25
+ end = open;
26
+ }
27
+ const code = text.charCodeAt(end - 1);
28
+ if (code >= 0xd800 && code <= 0xdbff)
29
+ end--;
30
+ }
31
+ chunks.push(text.slice(start, end));
32
+ start = end;
33
+ }
34
+ return chunks;
35
+ }
36
+ //# sourceMappingURL=chunks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunks.js","sourceRoot":"","sources":["../../src/translation/chunks.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,iFAAiF;AACjF,MAAM,UAAU,sBAAsB,CAAC,IAAY,EAAE,KAAa;IACjE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,EAAE;QACzC,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACxC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC5B,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC;QAC/C,IAAI,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YACvB,kEAAkE;YAClE,MAAM,KAAK,GAAG,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;YAC5C,KAAK,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;gBAClC,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;oBAC1C,GAAG,GAAG,CAAC,CAAC;oBACR,MAAM;gBACP,CAAC;YACF,CAAC;YACD,mEAAmE;YACnE,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;YAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACtC,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,GAAG,GAAG,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;gBACjD,IAAI,IAAI,KAAK,KAAK;oBACjB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;gBAC7D,GAAG,GAAG,IAAI,CAAC;YACZ,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;YACtC,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM;gBAAE,GAAG,EAAE,CAAC;QAC7C,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;QACpC,KAAK,GAAG,GAAG,CAAC;IACb,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Protect tokens that MT engines mangle (inline math, LaTeX commands, URLs,
3
+ * DOIs, citation markers, cross-references, and Markdown links) by swapping
4
+ * them for opaque placeholders before the request and restoring them afterwards.
5
+ */
6
+ export type MaskedToken = {
7
+ placeholder: string;
8
+ original: string;
9
+ };
10
+ export type MaskedText = {
11
+ text: string;
12
+ tokens: MaskedToken[];
13
+ };
14
+ export type RestoredText = {
15
+ text: string;
16
+ /** Placeholders the engine dropped or rewrote; caller decides retry policy. */
17
+ missing: number;
18
+ /** Duplicated or invented placeholders invalidate structural restoration. */
19
+ invalid?: boolean;
20
+ };
21
+ export declare function maskInlineTokens(text: string): MaskedText;
22
+ export declare function restoreInlineTokens(text: string, tokens: readonly MaskedToken[]): RestoredText;
@@ -0,0 +1,69 @@
1
+ // Ported from motif (Copyright (c) 2026 poco-ai, MIT). See MOTIF-LICENSE.txt.
2
+ /**
3
+ * Protect tokens that MT engines mangle (inline math, LaTeX commands, URLs,
4
+ * DOIs, citation markers, cross-references, and Markdown links) by swapping
5
+ * them for opaque placeholders before the request and restoring them afterwards.
6
+ */
7
+ import { findPreservedLinkSpans } from "./preserved-links.js";
8
+ /**
9
+ * Mathematical white square brackets: absent from paper prose, and left alone
10
+ * by MT engines far more reliably than ASCII brackets, which collide with
11
+ * citation markers and with the `[[n]]` batch numbering.
12
+ */
13
+ function placeholderFor(index) {
14
+ return `⟦${index}⟧`;
15
+ }
16
+ const MASK_PATTERNS = [
17
+ /⟪(?:F|\/?S)\d+⟫/gu,
18
+ // Display / inline TeX math (present when the source came from LaTeX).
19
+ /\$\$[^$]{1,400}\$\$/g,
20
+ /\$[^$\n]{1,200}\$/g,
21
+ /\\\((?:[^\\]|\\[^)]){1,200}?\\\)/g,
22
+ // LaTeX commands with or without a braced argument.
23
+ /\\[A-Za-z]+(?:\{[^{}]{0,120}\})?/g,
24
+ // Links and identifiers.
25
+ /https?:\/\/[^\s、,。]+/g,
26
+ /\bdoi:\s*10\.\d{4,9}\/\S+/gi,
27
+ /\b10\.\d{4,9}\/[^\s、,。]+/g,
28
+ ];
29
+ export function maskInlineTokens(text) {
30
+ // Match the original string once; overlapping patterns must not nest placeholders.
31
+ const spans = findPreservedLinkSpans(text).map(({ start, end, text }) => ({ start, end, text }));
32
+ for (const pattern of [...MASK_PATTERNS, /`[^`\n]+`/g, /⟦\s*\d+\s*⟧/g, /\[\[\d+\]\]/g]) {
33
+ for (const match of text.matchAll(new RegExp(pattern.source, pattern.flags))) {
34
+ spans.push({ start: match.index, end: match.index + match[0].length, text: match[0] });
35
+ }
36
+ }
37
+ spans.sort((a, b) => a.start - b.start || b.end - a.end);
38
+ const tokens = [];
39
+ let cursor = 0;
40
+ let masked = '';
41
+ let index = 0;
42
+ for (const span of spans) {
43
+ if (span.start < cursor)
44
+ continue;
45
+ let placeholder = placeholderFor(index++);
46
+ while (text.includes(placeholder))
47
+ placeholder = placeholderFor(index++);
48
+ tokens.push({ placeholder, original: span.text });
49
+ masked += text.slice(cursor, span.start) + placeholder;
50
+ cursor = span.end;
51
+ }
52
+ return { text: masked + text.slice(cursor), tokens };
53
+ }
54
+ export function restoreInlineTokens(text, tokens) {
55
+ const originals = new Map(tokens.map(token => [token.placeholder.slice(1, -1), token.original]));
56
+ const counts = new Map();
57
+ let invalid = false;
58
+ // One pass also prevents a literal placeholder in an original formula from being restored twice.
59
+ const out = text.replace(/⟦\s*(\d+)\s*⟧/g, (full, id) => {
60
+ counts.set(id, (counts.get(id) ?? 0) + 1);
61
+ const original = originals.get(id);
62
+ if (original === undefined || (counts.get(id) ?? 0) > 1)
63
+ invalid = true;
64
+ return original ?? full;
65
+ });
66
+ const missing = [...originals.keys()].filter(id => !counts.has(id)).length;
67
+ return { text: out, missing, ...(invalid ? { invalid: true } : {}) };
68
+ }
69
+ //# sourceMappingURL=mask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mask.js","sourceRoot":"","sources":["../../src/translation/mask.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /** Heal PDF ligatures and wrapped words without deleting unclassified page content. */
2
+ export declare function normalizeTranslationSource(raw: string): string;
3
+ export declare function translateDocumentText(input: string, target: string, translate: (text: string) => Promise<string>, signal: AbortSignal): Promise<string>;