pi-tuck 1.0.1 → 1.0.4
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/CHANGELOG.md +65 -3
- package/README.md +138 -57
- package/dist/index.d.ts +2 -1
- package/dist/index.js +7 -4
- package/dist/src/budget.d.ts +17 -4
- package/dist/src/budget.js +33 -10
- package/dist/src/build-id.d.ts +1 -1
- package/dist/src/build-id.js +1 -1
- package/dist/src/compaction.d.ts +7 -0
- package/dist/src/compaction.js +32 -16
- package/dist/src/diagnostics.d.ts +2 -1
- package/dist/src/omissions.d.ts +1 -1
- package/dist/src/omissions.js +8 -8
- package/dist/src/tuck/core/brief.d.ts +13 -43
- package/dist/src/tuck/core/brief.js +41 -255
- package/dist/src/tuck/core/filter-noise.js +2 -1
- package/dist/src/tuck/core/format.d.ts +2 -6
- package/dist/src/tuck/core/format.js +4 -10
- package/dist/src/tuck/core/normalize.js +22 -50
- package/dist/src/tuck/core/shrink.d.ts +33 -0
- package/dist/src/tuck/core/shrink.js +218 -0
- package/dist/src/tuck/core/summarize.d.ts +15 -7
- package/dist/src/tuck/core/summarize.js +17 -22
- package/dist/src/tuck/core/transcript.d.ts +24 -13
- package/dist/src/tuck/core/transcript.js +251 -83
- package/dist/src/tuck/types.d.ts +4 -21
- package/dist/src/tuck.d.ts +8 -10
- package/dist/src/tuck.js +23 -25
- package/index.ts +8 -5
- package/package.json +1 -1
- package/src/budget.ts +29 -9
- package/src/build-id.ts +1 -1
- package/src/compaction.ts +33 -14
- package/src/diagnostics.ts +1 -1
- package/src/omissions.ts +8 -8
- package/src/tuck/core/brief.ts +48 -265
- package/src/tuck/core/filter-noise.ts +2 -1
- package/src/tuck/core/format.ts +4 -11
- package/src/tuck/core/normalize.ts +21 -50
- package/src/tuck/core/shrink.ts +217 -0
- package/src/tuck/core/summarize.ts +26 -24
- package/src/tuck/core/transcript.ts +238 -82
- package/src/tuck/types.ts +9 -20
- package/src/tuck.ts +30 -32
- package/dist/src/tuck/core/brief-meter.d.ts +0 -42
- package/dist/src/tuck/core/brief-meter.js +0 -273
- package/dist/src/tuck/core/rank.d.ts +0 -42
- package/dist/src/tuck/core/rank.js +0 -292
- package/src/tuck/core/brief-meter.ts +0 -235
- package/src/tuck/core/rank.ts +0 -331
package/dist/src/compaction.js
CHANGED
|
@@ -5,7 +5,7 @@ import { exportSource } from "./source-export.js";
|
|
|
5
5
|
import { isDeepStrictEqual } from "node:util";
|
|
6
6
|
import { assertNativeToolRounds } from "./protocol.js";
|
|
7
7
|
import { projectedMessages } from "./omissions.js";
|
|
8
|
-
import { bandOf, chooseCut, conversationTokens, decideCut, gateCut, incomingEventTokens, incomingTokens, measurableConversationTokens, planningReserve, projectedConversationTokens, requestTokens, systemPart } from "./budget.js";
|
|
8
|
+
import { DEFAULT_CUT_AT, bandOf, chooseCut, conversationTokens, decideCut, gateCut, incomingEventTokens, incomingTokens, measurableConversationTokens, planningReserve, projectedConversationTokens, requestTokens, systemPart, validCutAt } from "./budget.js";
|
|
9
9
|
import { isContextOverflow } from "@earendil-works/pi-ai";
|
|
10
10
|
import { convertToLlm, SessionManager, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
11
11
|
import { Type } from "typebox";
|
|
@@ -36,8 +36,18 @@ const RecallInput = Type.Union([
|
|
|
36
36
|
]);
|
|
37
37
|
const SearchInput = Type.Object({ query, priorResults: Type.Optional(Type.Array(Type.Object({ entryId, sourceSha: Type.String({ minLength: 64, maxLength: 64 }), start: Type.Integer({ minimum: 0 }), end: Type.Integer({ minimum: 0 }) }, { additionalProperties: false }), { maxItems: 20 })) }, { additionalProperties: false });
|
|
38
38
|
const reply = (value) => ({ content: [{ type: "text", text: JSON.stringify(value) }], details: {} });
|
|
39
|
-
|
|
39
|
+
/** The compactor's verdict that this attempt cannot be made: the cut it needs cannot be made, or the request cannot fit. It is a decision about the
|
|
40
|
+
* state as it was, so it ends with the attempt (`forgetRefusal`) and the next attempt decides afresh. Any other failure is a fault in the compactor
|
|
41
|
+
* or its host, and stays until the session restarts. */
|
|
42
|
+
class Refusal extends Error {
|
|
40
43
|
}
|
|
44
|
+
/** A refusal for want of room, which the user can remedy: it carries advice. */
|
|
45
|
+
class CapacityError extends Refusal {
|
|
46
|
+
}
|
|
47
|
+
/** What a capacity refusal tells the operator. When the provider has counted far more input than pi-tuck's estimate, the refusal most
|
|
48
|
+
* likely comes from that counting (an upstream behind a router can spend much more context on tool schemas), so the notice says so. */
|
|
49
|
+
export const refusalNotice = (message, factor) => message + "; shorten the input, use a model with a larger window, or lower Pi's compaction.reserveTokens, then retry." +
|
|
50
|
+
(factor >= 1.5 ? ` This provider has counted up to ${factor.toFixed(1)}x the input pi-tuck estimates; through a router such as OpenRouter, a different upstream may count less.` : "");
|
|
41
51
|
/** The most a seed or an overflow raise may set the calibration factor to. A paired reply's own ratio is not bounded by it. */
|
|
42
52
|
export const CALIBRATION_CEILING = 1.5;
|
|
43
53
|
/** How many recent paired replies (and overflow bounds, and the seed while it is among them) the calibration factor is the maximum of. */
|
|
@@ -81,6 +91,9 @@ export function seedWindow(branch, key) {
|
|
|
81
91
|
return initialWindow(key);
|
|
82
92
|
}
|
|
83
93
|
export async function installCompaction(pi, options = {}) {
|
|
94
|
+
const cutAt = options.cutAt ?? DEFAULT_CUT_AT;
|
|
95
|
+
if (!validCutAt(cutAt))
|
|
96
|
+
throw new Error("Invalid cut point: cutAt must be a number above 0 and at most 1");
|
|
84
97
|
const semantic = options.semanticRecall ?? createSemanticRecall();
|
|
85
98
|
let semanticTransition = Promise.resolve();
|
|
86
99
|
let semanticRequest = 0;
|
|
@@ -101,7 +114,7 @@ export async function installCompaction(pi, options = {}) {
|
|
|
101
114
|
}
|
|
102
115
|
});
|
|
103
116
|
const captureDraft = (draft, ctx, reserve, reason, tokensBefore, retainCurrentInput, operatorManual, previousSummary, budget) => recorder.measureInline(() => {
|
|
104
|
-
const attached = attachReceipt(draft, reason, reserve, ctx.model?.contextWindow ?? 0, { responseReserveTokens: budget?.response, hostInputTokens: budget?.host, mandatoryInputTokens: budget?.mandatory, target: budget?.target, triggerMarginTokens: budget?.margin, calibrationFactor: factor, calibrationWindow: [...ratios], calibrationIdentity: identity, activeInputCeilingTokens: lastCeiling });
|
|
117
|
+
const attached = attachReceipt(draft, reason, reserve, ctx.model?.contextWindow ?? 0, { responseReserveTokens: budget?.response, hostInputTokens: budget?.host, mandatoryInputTokens: budget?.mandatory, target: budget?.target, triggerMarginTokens: budget?.margin, ...(cutAt === 1 ? {} : { cutAt }), calibrationFactor: factor, calibrationWindow: [...ratios], calibrationIdentity: identity, activeInputCeilingTokens: lastCeiling });
|
|
105
118
|
proposalId = receiptId(attached);
|
|
106
119
|
if (recorder.enabled) {
|
|
107
120
|
try {
|
|
@@ -204,7 +217,7 @@ export async function installCompaction(pi, options = {}) {
|
|
|
204
217
|
// The cut planner's raw reserve for the target limit, on the same factor as every other consumer.
|
|
205
218
|
const planReserve = (target, room, mandatory, ctx) => {
|
|
206
219
|
identityFor(ctx);
|
|
207
|
-
return planningReserve(ctx.model?.contextWindow ?? 0, target === "band" ? bandOf(room) : room, factor, mandatory);
|
|
220
|
+
return planningReserve(ctx.model?.contextWindow ?? 0, target === "band" ? bandOf(room, cutAt) : room, factor, mandatory);
|
|
208
221
|
};
|
|
209
222
|
pi.on("message_end", (event, ctx) => {
|
|
210
223
|
if (event.message.role !== "assistant")
|
|
@@ -246,7 +259,7 @@ export async function installCompaction(pi, options = {}) {
|
|
|
246
259
|
learn(ratio);
|
|
247
260
|
});
|
|
248
261
|
pi.on("agent_start", () => { admittedRun = true; pending = undefined; phase = "idle"; });
|
|
249
|
-
pi.on("agent_settled", () => { admittedRun = false; pending = undefined; phase = "idle"; });
|
|
262
|
+
pi.on("agent_settled", () => { admittedRun = false; pending = undefined; phase = "idle"; forgetRefusal(); });
|
|
250
263
|
const active = () => options.active?.() ?? true;
|
|
251
264
|
const settingsFor = () => options.settings ?? SettingsManager.inMemory(pi.getSettings());
|
|
252
265
|
// The standalone default reserves the model's catalogue maximum output: a transport may omit the output cap, and then only that
|
|
@@ -268,10 +281,10 @@ export async function installCompaction(pi, options = {}) {
|
|
|
268
281
|
throw new Error("Invalid active-input token ceiling");
|
|
269
282
|
return Math.min((ctx.model?.contextWindow ?? 0) - response, ceiling ?? Infinity);
|
|
270
283
|
};
|
|
271
|
-
// The exact room, and the band a cut is triggered at (budget.ts); every throw and every fault uses the exact room.
|
|
284
|
+
// The exact room, and the band a cut is triggered at (budget.ts, from this session's cutAt); every throw and every fault uses the exact room.
|
|
272
285
|
const limits = (ctx) => {
|
|
273
286
|
const { response } = requestReserve(ctx), room = inputRoom(response, ctx);
|
|
274
|
-
return { response, room, band: bandOf(room) };
|
|
287
|
+
return { response, room, band: bandOf(room, cutAt) };
|
|
275
288
|
};
|
|
276
289
|
const fits = (tokens, limit, ctx) => corrected(tokens, ctx) < limit;
|
|
277
290
|
// The saved conversation and a cut's quantities are counted by the one function the guard counts with (strict, as the context hook is).
|
|
@@ -304,13 +317,17 @@ export async function installCompaction(pi, options = {}) {
|
|
|
304
317
|
finally {
|
|
305
318
|
try {
|
|
306
319
|
if (notify)
|
|
307
|
-
ctx.ui.notify(failure.message
|
|
320
|
+
ctx.ui.notify(refusalNotice(failure.message, factor), "error");
|
|
308
321
|
}
|
|
309
322
|
finally {
|
|
310
323
|
ctx.abort();
|
|
311
324
|
}
|
|
312
325
|
}
|
|
313
326
|
};
|
|
327
|
+
// The one place a refusal is dropped: when its attempt has settled, when a prompt arrives, and when the operator asks for a cut. A run that no
|
|
328
|
+
// input started (a custom message) has only the first of these.
|
|
329
|
+
const forgetRefusal = () => { if (failure instanceof Refusal)
|
|
330
|
+
failure = undefined; };
|
|
314
331
|
const access = () => {
|
|
315
332
|
if (failure)
|
|
316
333
|
throw failure;
|
|
@@ -326,8 +343,7 @@ export async function installCompaction(pi, options = {}) {
|
|
|
326
343
|
pi.on("input", async (event, ctx) => {
|
|
327
344
|
if (!ctx.isIdle() || phase === "starting")
|
|
328
345
|
return; // a steering or follow-up message joins the running request
|
|
329
|
-
|
|
330
|
-
failure = undefined;
|
|
346
|
+
forgetRefusal();
|
|
331
347
|
phase = "decided";
|
|
332
348
|
try {
|
|
333
349
|
await preflight(ctx, incomingTokens(event.text, event.images), ctx.getSystemPrompt(), pi.getActiveTools());
|
|
@@ -401,8 +417,8 @@ export async function installCompaction(pi, options = {}) {
|
|
|
401
417
|
// Pi also labels the awaited idle incoming preflight "manual".
|
|
402
418
|
const incoming = pendingCut;
|
|
403
419
|
const operatorManual = event.reason === "manual" && !(incoming && !admittedRun);
|
|
404
|
-
if (operatorManual && !admittedRun
|
|
405
|
-
|
|
420
|
+
if (operatorManual && !admittedRun)
|
|
421
|
+
forgetRefusal();
|
|
406
422
|
access();
|
|
407
423
|
if (event.customInstructions?.trim()) {
|
|
408
424
|
recorder.stop("no-cut-custom-instructions");
|
|
@@ -453,7 +469,7 @@ export async function installCompaction(pi, options = {}) {
|
|
|
453
469
|
recorder.stop("no-cut");
|
|
454
470
|
ctx.ui.notify(decision.message, "warning");
|
|
455
471
|
if (!operatorManual)
|
|
456
|
-
fail(decision.capacity ?
|
|
472
|
+
fail(new (decision.capacity ? CapacityError : Refusal)(decision.message), ctx);
|
|
457
473
|
return { cancel: true };
|
|
458
474
|
}
|
|
459
475
|
catch (error) {
|
|
@@ -513,7 +529,7 @@ export async function installCompaction(pi, options = {}) {
|
|
|
513
529
|
if (cut.opportunistic && !failure && !cut.proposed)
|
|
514
530
|
resolve();
|
|
515
531
|
else
|
|
516
|
-
reject(error.message === "Compaction cancelled" && failure instanceof
|
|
532
|
+
reject(error.message === "Compaction cancelled" && failure instanceof Refusal ? failure : error);
|
|
517
533
|
} }));
|
|
518
534
|
}
|
|
519
535
|
finally {
|
|
@@ -523,7 +539,7 @@ export async function installCompaction(pi, options = {}) {
|
|
|
523
539
|
// The cut awaited: take the snapshot again. An opportunistic cut never throws; the request is not over the room, and the guard decides.
|
|
524
540
|
const next = snapshot(), after = next.before + next.mandatory, afterFits = fits(after, limits(ctx).room, ctx);
|
|
525
541
|
if (!cut.opportunistic && (after >= total || !afterFits))
|
|
526
|
-
throw new (afterFits ?
|
|
542
|
+
throw new (afterFits ? Refusal : CapacityError)("Incoming Pi preflight compaction did not reduce context within the estimated request budget");
|
|
527
543
|
};
|
|
528
544
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
529
545
|
const prompted = phase === "decided";
|
|
@@ -580,7 +596,7 @@ export async function installCompaction(pi, options = {}) {
|
|
|
580
596
|
},
|
|
581
597
|
});
|
|
582
598
|
if (decision.kind === "fault")
|
|
583
|
-
throw new (decision.capacity ? CapacityError :
|
|
599
|
+
throw new (decision.capacity ? CapacityError : Refusal)(decision.message);
|
|
584
600
|
if (decision.kind !== "cut")
|
|
585
601
|
return;
|
|
586
602
|
const compaction = captureDraft(decision.plan.compaction, ctx, decision.plan.reserve, "tool-loop", before, true, false, previousSummary, { response, host, mandatory, target: decision.target, margin: room - band });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type RecallPolicy } from './recall.js';
|
|
2
2
|
import type { SessionEntry } from '@earendil-works/pi-coding-agent';
|
|
3
|
-
export declare const DIAGNOSTIC_BUILD = "
|
|
3
|
+
export declare const DIAGNOSTIC_BUILD = "fb0ab667f5bfcd22fd5cc6edb89cd859426fd7c28bb58c2bc7b9fbec6fcdd3da";
|
|
4
4
|
export interface Receipt {
|
|
5
5
|
id: string;
|
|
6
6
|
build: string;
|
|
@@ -15,6 +15,7 @@ export interface Receipt {
|
|
|
15
15
|
mandatoryInputTokens?: number;
|
|
16
16
|
target?: 'band' | 'room';
|
|
17
17
|
triggerMarginTokens?: number;
|
|
18
|
+
cutAt?: number;
|
|
18
19
|
calibrationFactor?: number;
|
|
19
20
|
calibrationWindow?: number[];
|
|
20
21
|
calibrationIdentity?: string;
|
package/dist/src/omissions.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { ExtensionContext, SessionEntry } from '@earendil-works/pi-coding-a
|
|
|
2
2
|
import { type RecallPolicy } from './recall.js';
|
|
3
3
|
/** Native branch edits supply exclusion; original global indices supply provenance. */
|
|
4
4
|
export declare function omittedSourceIndices(entries: readonly SessionEntry[], branch: readonly SessionEntry[]): ReadonlySet<number>;
|
|
5
|
-
/** Preserve surviving record bytes/order. No text matching or semantic inference. */
|
|
5
|
+
/** Preserve surviving record bytes/order, and the summary's format (a v1 summary stays v1). No text matching or semantic inference. */
|
|
6
6
|
export declare function filterOmittedSummary(summary: string, omitted: ReadonlySet<number>): string;
|
|
7
7
|
/** Custom quotes carry native entry identity; no text matching or legacy fallback. */
|
|
8
8
|
export declare function filterCustomQuotes(summary: string, entries: readonly SessionEntry[], branch: readonly SessionEntry[], policy?: RecallPolicy): string;
|
package/dist/src/omissions.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buildGlobalIndexById } from './tuck/core/global-indices.js';
|
|
2
|
-
import {
|
|
2
|
+
import { parseRecords, splitSummary, transcriptHeader } from './tuck/core/transcript.js';
|
|
3
3
|
import { createRecall } from './recall.js';
|
|
4
4
|
/** Native branch edits supply exclusion; original global indices supply provenance. */
|
|
5
5
|
export function omittedSourceIndices(entries, branch) {
|
|
@@ -13,24 +13,24 @@ export function omittedSourceIndices(entries, branch) {
|
|
|
13
13
|
}
|
|
14
14
|
return new Set([...edits].flatMap(([id, omitted]) => omitted && indices.has(id) ? [indices.get(id)] : []));
|
|
15
15
|
}
|
|
16
|
-
/** Preserve surviving record bytes/order. No text matching or semantic inference. */
|
|
16
|
+
/** Preserve surviving record bytes/order, and the summary's format (a v1 summary stays v1). No text matching or semantic inference. */
|
|
17
17
|
export function filterOmittedSummary(summary, omitted) {
|
|
18
18
|
if (!omitted.size)
|
|
19
19
|
return summary;
|
|
20
20
|
const split = splitSummary(summary);
|
|
21
21
|
if (!split)
|
|
22
22
|
throw new Error('Cannot apply native source omission to an unsupported compaction summary');
|
|
23
|
-
const { body, closing } = split;
|
|
24
|
-
const records = parseRecords(body), lines = body ? body.split('\n') : [];
|
|
23
|
+
const { body, closing, version } = split;
|
|
24
|
+
const records = parseRecords(body, version), lines = body ? body.split('\n') : [];
|
|
25
25
|
const retained = records.flatMap((record, i) => (record.sourceIndex !== undefined && omitted.has(record.sourceIndex)) || record.sourceIndices?.some(index => omitted.has(index)) ? [] : [lines[i]]);
|
|
26
|
-
return
|
|
26
|
+
return transcriptHeader(version) + '\n' + retained.join('\n') + closing;
|
|
27
27
|
}
|
|
28
28
|
/** Custom quotes carry native entry identity; no text matching or legacy fallback. */
|
|
29
29
|
export function filterCustomQuotes(summary, entries, branch, policy = {}) {
|
|
30
30
|
const split = splitSummary(summary);
|
|
31
31
|
if (!split)
|
|
32
32
|
throw new Error('Cannot resolve custom sources in an unsupported compaction summary');
|
|
33
|
-
const { body, closing } = split;
|
|
33
|
+
const { body, closing, version } = split;
|
|
34
34
|
const omitted = new Set(branch.flatMap(entry => entry.type === 'context_edit' && entry.replacement === null ? [entry.targetId] : []));
|
|
35
35
|
const eligible = createRecall(policy).isHistorySource, indices = buildGlobalIndexById(entries);
|
|
36
36
|
const globalCounts = new Map(), branchSources = new Map();
|
|
@@ -42,7 +42,7 @@ export function filterCustomQuotes(summary, entries, branch, policy = {}) {
|
|
|
42
42
|
branchSources.set(entry.id, found);
|
|
43
43
|
}
|
|
44
44
|
const lines = body ? body.split('\n') : [];
|
|
45
|
-
const records = parseRecords(body);
|
|
45
|
+
const records = parseRecords(body, version);
|
|
46
46
|
const retained = records.flatMap((record, i) => {
|
|
47
47
|
if (record.role !== 'custom')
|
|
48
48
|
return [lines[i]];
|
|
@@ -56,7 +56,7 @@ export function filterCustomQuotes(summary, entries, branch, policy = {}) {
|
|
|
56
56
|
return [];
|
|
57
57
|
return [lines[i]];
|
|
58
58
|
});
|
|
59
|
-
return
|
|
59
|
+
return transcriptHeader(version) + '\n' + retained.join('\n') + closing;
|
|
60
60
|
}
|
|
61
61
|
/** The saved conversation as this compactor delivers it: native omissions and ineligible custom quotes removed from summaries. */
|
|
62
62
|
export function projectedMessages(messages, ctx, policy) {
|
|
@@ -1,49 +1,19 @@
|
|
|
1
1
|
import type { NormalizedBlock } from "../types.js";
|
|
2
|
-
import type { SourceTime } from "./source-time.js";
|
|
3
2
|
import { type TranscriptRecord } from "./transcript.js";
|
|
4
|
-
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
* If `lines[i]` opens a heredoc whose terminator actually appears on a later
|
|
8
|
-
* line, return that terminator's line index; otherwise -1. Callers use -1 to
|
|
9
|
-
* leave following lines intact instead of treating a stray `<<` (a shift op, a
|
|
10
|
-
* quoted string, or a truncated body) as a heredoc and skipping real commands.
|
|
11
|
-
*/
|
|
12
|
-
export declare const heredocCloseIndex: (lines: string[], i: number) => number;
|
|
13
|
-
export type CallRecord = Extract<TranscriptRecord, {
|
|
14
|
-
kind: "tool_call";
|
|
3
|
+
/** The user, assistant and notice blocks of a dialogue; tool blocks are never rendered. */
|
|
4
|
+
export type DialogueBlock = Extract<NormalizedBlock, {
|
|
5
|
+
kind: "user" | "assistant" | "notice";
|
|
15
6
|
}>;
|
|
16
|
-
|
|
17
|
-
export type ExcerptCache = WeakMap<NormalizedBlock, Map<string, TranscriptRecord[]>>;
|
|
18
|
-
/** The assistant texts that close a segment: no further text before the next user turn or the end of the chronology. */
|
|
19
|
-
export declare const segmentClosing: (chronology: NormalizedBlock[]) => Set<NormalizedBlock>;
|
|
7
|
+
export declare const isDialogue: (block: NormalizedBlock) => block is DialogueBlock;
|
|
20
8
|
/** A user or assistant text block the omission notices count when it is unselected: non-empty, and not rendered in the mandatory base. */
|
|
21
9
|
export declare const isOmittable: (block: NormalizedBlock, mandatorySources?: ReadonlySet<number>) => boolean;
|
|
22
|
-
/** `sources` are the distinct locators of the omitted blocks in ascending order; at most the first 9 decide the notice.
|
|
10
|
+
/** `sources` are the distinct locators of the omitted blocks in ascending order; at most the first 9 decide the notice. They are written
|
|
11
|
+
* as "#N" on the record's line. */
|
|
23
12
|
export declare const omissionRecord: (role: "user" | "assistant", count: number, sources: number[], unlocated: boolean) => TranscriptRecord;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
export declare const
|
|
29
|
-
/**
|
|
30
|
-
export
|
|
31
|
-
full: boolean;
|
|
32
|
-
closing: boolean;
|
|
33
|
-
mandatory?: ReadonlySet<number>;
|
|
34
|
-
bounded: boolean;
|
|
35
|
-
covered: boolean;
|
|
36
|
-
}
|
|
37
|
-
/** The records of one block alone: before identical calls merge and before source times are attached. */
|
|
38
|
-
export declare const blockRecords: (b: NormalizedBlock, view: BlockView, cache?: ExcerptCache) => TranscriptRecord[];
|
|
39
|
-
/** Whether `record` is the same call as the `previous` record, shown a second time. */
|
|
40
|
-
export declare const repeatsCall: (previous: TranscriptRecord | undefined, record: TranscriptRecord) => previous is CallRecord & {
|
|
41
|
-
sourceIndex: number;
|
|
42
|
-
};
|
|
43
|
-
/** The earlier record of a repeated call, now also locating the later one. */
|
|
44
|
-
export declare const absorbCall: (previous: TranscriptRecord, record: TranscriptRecord) => TranscriptRecord;
|
|
45
|
-
/** A source time for each locator the record names; only briefs of times-carrying blocks have them. */
|
|
46
|
-
export declare const noticeTimes: (blocks: NormalizedBlock[]) => Map<string, SourceTime>;
|
|
47
|
-
export declare const decorateRecord: (record: TranscriptRecord, times: NormalizedBlock["sourceTimes"], entryTimes: ReadonlyMap<string, SourceTime>) => TranscriptRecord;
|
|
48
|
-
export declare const buildBriefRecords: (blocks: NormalizedBlock[], cache?: ExcerptCache) => TranscriptRecord[];
|
|
49
|
-
export declare const compileBrief: (blocks: NormalizedBlock[], cache?: ExcerptCache) => string;
|
|
13
|
+
/** The word budgets of a clipped excerpt: the passages kept from the start of the text, and from its end. */
|
|
14
|
+
export type Clip = readonly [headWords: number, tailWords: number];
|
|
15
|
+
/** The records of one dialogue block alone, before source times are attached: its whole original text, or, with a `clip`, the excerpts
|
|
16
|
+
* that keep their sanitized-block spans around an omission. A mandatory source renders nothing: the adapter quotes it in its base. */
|
|
17
|
+
export declare const blockRecords: (b: DialogueBlock, mandatory: ReadonlySet<number> | undefined, clip?: Clip) => TranscriptRecord[];
|
|
18
|
+
/** A source time for each locator a user record names; only briefs of times-carrying blocks have them. */
|
|
19
|
+
export declare const decorateRecord: (record: TranscriptRecord, times: NormalizedBlock["sourceTimes"]) => TranscriptRecord;
|