@seanhogg/builderforce-memory 2026.9.32 → 2026.9.34

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.
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Token estimate + budget math for the hand-off packer.
3
+ *
4
+ * The estimate matches Builderforce's gateway heuristic (`workingTranscript.ts`
5
+ * `messageTokens`): a string content's length / 4, a non-string content's JSON length / 4,
6
+ * plus any `tool_calls` payload's JSON length, plus 4 tokens of role/framing overhead.
7
+ */
8
+ import type { HandoffBudgetInput, HandoffMessage, TokenEstimator } from './types.js';
9
+ /** Per-message role/framing overhead, in tokens. */
10
+ export declare const MESSAGE_OVERHEAD_TOKENS = 4;
11
+ /** Default estimator — chars/4, rounded up. */
12
+ export declare const defaultEstimateTokens: TokenEstimator;
13
+ /**
14
+ * The tokens a hand-off may use: the next model's window less the reply reserve and the
15
+ * advertised tool schemas. Never negative.
16
+ */
17
+ export declare function handoffBudget(input: HandoffBudgetInput): number;
18
+ /** A message's content as text: the string itself, else its JSON. */
19
+ export declare function contentText(content: unknown): string;
20
+ /** Does this assistant message carry tool calls? */
21
+ export declare function hasToolCalls(m: HandoffMessage): boolean;
22
+ /** Estimated tokens for one message (content + tool-call payload + framing). */
23
+ export declare function messageTokens(m: HandoffMessage, estimate: TokenEstimator): number;
24
+ /** Estimated tokens for a list of messages. */
25
+ export declare function tokensOf(msgs: readonly HandoffMessage[], estimate: TokenEstimator): number;
26
+ /**
27
+ * The longest prefix of `text` (with a trailing ellipsis when cut) whose estimate is at
28
+ * most `maxTokens`; '' when not even one character fits. Binary search, so it works for
29
+ * any monotone estimator, not only chars/4.
30
+ */
31
+ export declare function fitText(text: string, maxTokens: number, estimate: TokenEstimator): string;
32
+ /** A char budget a `recall` callback can honour for `tokens` tokens under `estimate`. */
33
+ export declare function charsForTokens(tokens: number, estimate: TokenEstimator): number;
34
+ //# sourceMappingURL=budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../../src/handoff/budget.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAErF,oDAAoD;AACpD,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,+CAA+C;AAC/C,eAAO,MAAM,qBAAqB,EAAE,cAAqD,CAAC;AAE1F;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAG/D;AAED,qEAAqE;AACrE,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAGpD;AAED,oDAAoD;AACpD,wBAAgB,YAAY,CAAC,CAAC,EAAE,cAAc,GAAG,OAAO,CAGvD;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,CAAC,EAAE,cAAc,EAAE,QAAQ,EAAE,cAAc,GAAG,MAAM,CAIjF;AAED,+CAA+C;AAC/C,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,cAAc,EAAE,EAAE,QAAQ,EAAE,cAAc,GAAG,MAAM,CAI1F;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,MAAM,CAWzF;AAED,yFAAyF;AACzF,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,MAAM,CAO/E"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Token estimate + budget math for the hand-off packer.
3
+ *
4
+ * The estimate matches Builderforce's gateway heuristic (`workingTranscript.ts`
5
+ * `messageTokens`): a string content's length / 4, a non-string content's JSON length / 4,
6
+ * plus any `tool_calls` payload's JSON length, plus 4 tokens of role/framing overhead.
7
+ */
8
+ /** Per-message role/framing overhead, in tokens. */
9
+ export const MESSAGE_OVERHEAD_TOKENS = 4;
10
+ /** Default estimator — chars/4, rounded up. */
11
+ export const defaultEstimateTokens = (text) => Math.ceil(text.length / 4);
12
+ /**
13
+ * The tokens a hand-off may use: the next model's window less the reply reserve and the
14
+ * advertised tool schemas. Never negative.
15
+ */
16
+ export function handoffBudget(input) {
17
+ const budget = input.targetContextTokens - input.reserveOutputTokens - (input.toolsTokens ?? 0);
18
+ return Math.max(0, Math.floor(budget));
19
+ }
20
+ /** A message's content as text: the string itself, else its JSON. */
21
+ export function contentText(content) {
22
+ if (typeof content === 'string')
23
+ return content;
24
+ return JSON.stringify(content ?? '') ?? '';
25
+ }
26
+ /** Does this assistant message carry tool calls? */
27
+ export function hasToolCalls(m) {
28
+ if (m.tool_calls == null)
29
+ return false;
30
+ return Array.isArray(m.tool_calls) ? m.tool_calls.length > 0 : true;
31
+ }
32
+ /** Estimated tokens for one message (content + tool-call payload + framing). */
33
+ export function messageTokens(m, estimate) {
34
+ let text = contentText(m.content);
35
+ if (m.tool_calls != null)
36
+ text += JSON.stringify(m.tool_calls) ?? '';
37
+ return estimate(text) + MESSAGE_OVERHEAD_TOKENS;
38
+ }
39
+ /** Estimated tokens for a list of messages. */
40
+ export function tokensOf(msgs, estimate) {
41
+ let total = 0;
42
+ for (const m of msgs)
43
+ total += messageTokens(m, estimate);
44
+ return total;
45
+ }
46
+ /**
47
+ * The longest prefix of `text` (with a trailing ellipsis when cut) whose estimate is at
48
+ * most `maxTokens`; '' when not even one character fits. Binary search, so it works for
49
+ * any monotone estimator, not only chars/4.
50
+ */
51
+ export function fitText(text, maxTokens, estimate) {
52
+ if (maxTokens <= 0)
53
+ return '';
54
+ if (estimate(text) <= maxTokens)
55
+ return text;
56
+ let lo = 0;
57
+ let hi = text.length;
58
+ while (lo < hi) {
59
+ const mid = Math.ceil((lo + hi) / 2);
60
+ if (estimate(`${text.slice(0, mid)}…`) <= maxTokens)
61
+ lo = mid;
62
+ else
63
+ hi = mid - 1;
64
+ }
65
+ return lo > 0 ? `${text.slice(0, lo).trimEnd()}…` : '';
66
+ }
67
+ /** A char budget a `recall` callback can honour for `tokens` tokens under `estimate`. */
68
+ export function charsForTokens(tokens, estimate) {
69
+ if (tokens <= 0)
70
+ return 0;
71
+ // Calibrate on a probe so a custom estimator gets a proportionate budget; fitText
72
+ // enforces the token ceiling on whatever comes back either way.
73
+ const probe = 'x'.repeat(400);
74
+ const perChar = Math.max(estimate(probe), 1) / probe.length;
75
+ return Math.max(0, Math.floor(tokens / perChar));
76
+ }
77
+ //# sourceMappingURL=budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget.js","sourceRoot":"","sources":["../../src/handoff/budget.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Model hand-off packing — the `@seanhogg/builderforce-memory/handoff` subpath (PRD 34 §3.3).
3
+ *
4
+ * When a conversation moves to another model (a local one with a small window, say),
5
+ * {@link packHandoff} fits the transcript into that model's budget: the system message(s)
6
+ * stay, the latest user directive stays verbatim, tool calls are never split from their
7
+ * results, the verbatim tail takes what the budget allows, and the dropped middle becomes
8
+ * ONE assistant note (summary → memory recall → elision marker, with a one-line hand-off
9
+ * note when the models differ).
10
+ *
11
+ * Pure: no WebGPU, no IndexedDB, no `node:*` — runs in a Cloudflare Worker, a browser
12
+ * page and Node alike.
13
+ */
14
+ export { packHandoff } from './pack.js';
15
+ export { handoffBudget, defaultEstimateTokens, messageTokens, tokensOf } from './budget.js';
16
+ export { handoffLine, elisionMarker } from './note.js';
17
+ export type { HandoffMessage, HandoffPackInput, HandoffPack, HandoffBudgetInput, TokenEstimator, } from './types.js';
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handoff/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACvD,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,cAAc,GACf,MAAM,YAAY,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Model hand-off packing — the `@seanhogg/builderforce-memory/handoff` subpath (PRD 34 §3.3).
3
+ *
4
+ * When a conversation moves to another model (a local one with a small window, say),
5
+ * {@link packHandoff} fits the transcript into that model's budget: the system message(s)
6
+ * stay, the latest user directive stays verbatim, tool calls are never split from their
7
+ * results, the verbatim tail takes what the budget allows, and the dropped middle becomes
8
+ * ONE assistant note (summary → memory recall → elision marker, with a one-line hand-off
9
+ * note when the models differ).
10
+ *
11
+ * Pure: no WebGPU, no IndexedDB, no `node:*` — runs in a Cloudflare Worker, a browser
12
+ * page and Node alike.
13
+ */
14
+ export { packHandoff } from './pack.js';
15
+ export { handoffBudget, defaultEstimateTokens, messageTokens, tokensOf } from './budget.js';
16
+ export { handoffLine, elisionMarker } from './note.js';
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/handoff/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The ONE assistant note that stands in for the dropped middle of a handed-off
3
+ * transcript: an optional one-line hand-off note, then the first body source that
4
+ * succeeds — the `summarize` result, else the `recall` memory block for the latest
5
+ * directive, else an elision marker. A failing, timing-out or empty source falls through
6
+ * to the next; nothing here throws for those.
7
+ */
8
+ import type { HandoffMessage, HandoffPackInput, TokenEstimator } from './types.js';
9
+ /** The hand-off line, or '' when either ref is missing or they are the same model. */
10
+ export declare function handoffLine(from?: string, to?: string): string;
11
+ /** The marker for `n` messages that were dropped with nothing to stand in for them. */
12
+ export declare function elisionMarker(n: number): string;
13
+ /** Header + body as one note text. */
14
+ export declare function composeNote(header: string, body: string): string;
15
+ /** Tokens the note message costs, framing included. */
16
+ export declare function noteTokens(text: string, estimate: TokenEstimator): number;
17
+ export interface NoteRequest {
18
+ header: string;
19
+ dropped: readonly HandoffMessage[];
20
+ directive: HandoffMessage | undefined;
21
+ /** Tokens the note message may cost, framing included. */
22
+ room: number;
23
+ estimate: TokenEstimator;
24
+ summarize?: HandoffPackInput['summarize'];
25
+ recall?: HandoffPackInput['recall'];
26
+ timeoutMs?: number;
27
+ }
28
+ export interface NoteResult {
29
+ text: string;
30
+ summarized: boolean;
31
+ recalled: boolean;
32
+ }
33
+ /** Build the note, or null when not even the elision marker fits `room`. */
34
+ export declare function buildNote(req: NoteRequest): Promise<NoteResult | null>;
35
+ //# sourceMappingURL=note.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"note.d.ts","sourceRoot":"","sources":["../../src/handoff/note.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEnF,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,sCAAsC;AACtC,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,uDAAuD;AACvD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,MAAM,CAEzE;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,SAAS,cAAc,EAAE,CAAC;IACnC,SAAS,EAAE,cAAc,GAAG,SAAS,CAAC;IACtC,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,SAAS,CAAC,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAC1C,MAAM,CAAC,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAgCD,4EAA4E;AAC5E,wBAAsB,SAAS,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAqB5E"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * The ONE assistant note that stands in for the dropped middle of a handed-off
3
+ * transcript: an optional one-line hand-off note, then the first body source that
4
+ * succeeds — the `summarize` result, else the `recall` memory block for the latest
5
+ * directive, else an elision marker. A failing, timing-out or empty source falls through
6
+ * to the next; nothing here throws for those.
7
+ */
8
+ import { MESSAGE_OVERHEAD_TOKENS, charsForTokens, contentText, fitText } from './budget.js';
9
+ /** The hand-off line, or '' when either ref is missing or they are the same model. */
10
+ export function handoffLine(from, to) {
11
+ return from && to && from !== to ? `Handed off from ${from} to ${to}.` : '';
12
+ }
13
+ /** The marker for `n` messages that were dropped with nothing to stand in for them. */
14
+ export function elisionMarker(n) {
15
+ return `[${n} earlier messages omitted]`;
16
+ }
17
+ /** Header + body as one note text. */
18
+ export function composeNote(header, body) {
19
+ return header ? `${header}\n${body}` : body;
20
+ }
21
+ /** Tokens the note message costs, framing included. */
22
+ export function noteTokens(text, estimate) {
23
+ return estimate(text) + MESSAGE_OVERHEAD_TOKENS;
24
+ }
25
+ /** Settle `run()` to its trimmed string, or '' on throw, rejection, timeout or non-string. */
26
+ async function attempt(run, timeoutMs) {
27
+ let timer;
28
+ try {
29
+ const work = Promise.resolve().then(run);
30
+ const raced = timeoutMs && timeoutMs > 0
31
+ ? Promise.race([work, new Promise((resolve) => { timer = setTimeout(() => resolve(''), timeoutMs); })])
32
+ : work;
33
+ const out = await raced;
34
+ return typeof out === 'string' ? out.trim() : '';
35
+ }
36
+ catch {
37
+ return '';
38
+ }
39
+ finally {
40
+ if (timer !== undefined)
41
+ clearTimeout(timer);
42
+ }
43
+ }
44
+ /** `body` cut so header + body fits `room`; '' when nothing of it fits. */
45
+ function fitBody(req, body) {
46
+ const headerTokens = req.header ? req.estimate(`${req.header}\n`) : 0;
47
+ let max = req.room - MESSAGE_OVERHEAD_TOKENS - headerTokens;
48
+ // The estimator need not be additive; shrink until the composed note really fits.
49
+ for (let tries = 0; tries < 16 && max > 0; tries++, max--) {
50
+ const fitted = fitText(body, max, req.estimate);
51
+ if (!fitted)
52
+ return '';
53
+ if (noteTokens(composeNote(req.header, fitted), req.estimate) <= req.room)
54
+ return fitted;
55
+ }
56
+ return '';
57
+ }
58
+ /** Build the note, or null when not even the elision marker fits `room`. */
59
+ export async function buildNote(req) {
60
+ if (req.summarize) {
61
+ const summarize = req.summarize;
62
+ const summary = fitBody(req, await attempt(() => summarize(req.dropped), req.timeoutMs));
63
+ if (summary)
64
+ return { text: composeNote(req.header, summary), summarized: true, recalled: false };
65
+ }
66
+ if (req.recall && req.directive) {
67
+ const recall = req.recall;
68
+ const headerTokens = req.header ? req.estimate(`${req.header}\n`) : 0;
69
+ const budgetChars = charsForTokens(req.room - MESSAGE_OVERHEAD_TOKENS - headerTokens, req.estimate);
70
+ if (budgetChars > 0) {
71
+ const query = contentText(req.directive.content);
72
+ const block = fitBody(req, await attempt(() => recall(query, budgetChars), req.timeoutMs));
73
+ if (block)
74
+ return { text: composeNote(req.header, block), summarized: false, recalled: true };
75
+ }
76
+ }
77
+ const marker = composeNote(req.header, elisionMarker(req.dropped.length));
78
+ if (noteTokens(marker, req.estimate) <= req.room)
79
+ return { text: marker, summarized: false, recalled: false };
80
+ return null;
81
+ }
82
+ //# sourceMappingURL=note.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"note.js","sourceRoot":"","sources":["../../src/handoff/note.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `packHandoff` — fit a transcript into the NEXT model's window (PRD 34 §3.3).
3
+ *
4
+ * Shape of the result, mirroring Builderforce's `assembleCompacted`
5
+ * (brain-embedded/src/workingTranscript.ts):
6
+ *
7
+ * system message(s) · ONE assistant note for the dropped middle · the latest user
8
+ * directive re-injected verbatim (only when it fell in the middle) · the verbatim tail
9
+ *
10
+ * The tail is the longest run of whole units (see `segments.ts`) from the end that fits
11
+ * what is left after the system message(s), the directive and room for an elision marker.
12
+ * Whatever room the tail leaves is the note's: summary, else recall, else the marker.
13
+ * The result never exceeds the budget unless the system message(s) + directive alone do.
14
+ */
15
+ import type { HandoffPack, HandoffPackInput } from './types.js';
16
+ export declare function packHandoff(input: HandoffPackInput): Promise<HandoffPack>;
17
+ //# sourceMappingURL=pack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack.d.ts","sourceRoot":"","sources":["../../src/handoff/pack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAkB,WAAW,EAAE,gBAAgB,EAAkB,MAAM,YAAY,CAAC;AA6DhG,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAqD/E"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * `packHandoff` — fit a transcript into the NEXT model's window (PRD 34 §3.3).
3
+ *
4
+ * Shape of the result, mirroring Builderforce's `assembleCompacted`
5
+ * (brain-embedded/src/workingTranscript.ts):
6
+ *
7
+ * system message(s) · ONE assistant note for the dropped middle · the latest user
8
+ * directive re-injected verbatim (only when it fell in the middle) · the verbatim tail
9
+ *
10
+ * The tail is the longest run of whole units (see `segments.ts`) from the end that fits
11
+ * what is left after the system message(s), the directive and room for an elision marker.
12
+ * Whatever room the tail leaves is the note's: summary, else recall, else the marker.
13
+ * The result never exceeds the budget unless the system message(s) + directive alone do.
14
+ */
15
+ import { defaultEstimateTokens, handoffBudget, messageTokens, tokensOf } from './budget.js';
16
+ import { buildNote, composeNote, elisionMarker, handoffLine, noteTokens } from './note.js';
17
+ import { segmentTranscript } from './segments.js';
18
+ /** Index of the last `user` message, or -1. */
19
+ function latestDirectiveIndex(convo) {
20
+ for (let i = convo.length - 1; i >= 0; i--)
21
+ if (convo[i].role === 'user')
22
+ return i;
23
+ return -1;
24
+ }
25
+ /**
26
+ * Longest suffix of whole segments that fits `room`, counting the directive's cost
27
+ * separately while it still sits before the suffix. Never leads with a `tool` result.
28
+ */
29
+ function selectTail(convo, segs, directiveIdx, directiveTokens, room) {
30
+ let start = convo.length;
31
+ let tokens = 0;
32
+ let first = segs.length;
33
+ for (let s = segs.length - 1; s >= 0; s--) {
34
+ const seg = segs[s];
35
+ const directiveOutside = directiveIdx >= 0 && directiveIdx < seg.start;
36
+ if (tokens + seg.tokens + (directiveOutside ? directiveTokens : 0) > room)
37
+ break;
38
+ tokens += seg.tokens;
39
+ start = seg.start;
40
+ first = s;
41
+ }
42
+ // Only an orphan result at the very front can lead a unit with `tool`; drop it.
43
+ while (first < segs.length && convo[segs[first].start].role === 'tool') {
44
+ tokens -= segs[first].tokens;
45
+ first += 1;
46
+ start = first < segs.length ? segs[first].start : convo.length;
47
+ }
48
+ return { start, tokens };
49
+ }
50
+ function result(messages, dropped, estimate, extra = {}) {
51
+ return {
52
+ messages,
53
+ dropped,
54
+ summarized: extra.summarized ?? false,
55
+ recalled: extra.recalled ?? false,
56
+ estimatedTokens: tokensOf(messages, estimate),
57
+ fits: extra.fits ?? true,
58
+ };
59
+ }
60
+ export async function packHandoff(input) {
61
+ const estimate = input.estimateTokens ?? defaultEstimateTokens;
62
+ const budget = handoffBudget(input);
63
+ const all = input.messages;
64
+ // Under budget: hand the transcript over untouched.
65
+ if (tokensOf(all, estimate) <= budget)
66
+ return result(all.slice(), 0, estimate);
67
+ const system = all.filter((m) => m.role === 'system');
68
+ const convo = all.filter((m) => m.role !== 'system');
69
+ const systemTokens = tokensOf(system, estimate);
70
+ const directiveIdx = latestDirectiveIndex(convo);
71
+ const directive = directiveIdx >= 0 ? convo[directiveIdx] : undefined;
72
+ const directiveTokens = directive ? messageTokens(directive, estimate) : 0;
73
+ // The irreducible core does not fit: send it anyway and say so.
74
+ if (systemTokens + directiveTokens > budget) {
75
+ const core = directive ? [...system, directive] : [...system];
76
+ return result(core, convo.length - (directive ? 1 : 0), estimate, { fits: false });
77
+ }
78
+ // Reserve room for the smallest note (the marker, at its widest count) when it fits.
79
+ const header = handoffLine(input.from, input.to);
80
+ const minNote = noteTokens(composeNote(header, elisionMarker(convo.length)), estimate);
81
+ const noteFits = systemTokens + directiveTokens + minNote <= budget;
82
+ const room = budget - systemTokens - (noteFits ? minNote : 0);
83
+ const tail = selectTail(convo, segmentTranscript(convo, estimate), directiveIdx, directiveTokens, room);
84
+ const reinject = directive !== undefined && directiveIdx < tail.start;
85
+ const droppedMsgs = convo.slice(0, tail.start).filter((_, i) => !(reinject && i === directiveIdx));
86
+ const used = systemTokens + tail.tokens + (reinject ? directiveTokens : 0);
87
+ const note = droppedMsgs.length > 0
88
+ ? await buildNote({
89
+ header,
90
+ dropped: droppedMsgs,
91
+ directive,
92
+ room: budget - used,
93
+ estimate,
94
+ summarize: input.summarize,
95
+ recall: input.recall,
96
+ timeoutMs: input.timeoutMs,
97
+ })
98
+ : null;
99
+ const messages = [...system];
100
+ if (note)
101
+ messages.push({ role: 'assistant', content: note.text });
102
+ if (reinject && directive)
103
+ messages.push(directive);
104
+ messages.push(...convo.slice(tail.start));
105
+ return result(messages, droppedMsgs.length, estimate, {
106
+ summarized: note?.summarized ?? false,
107
+ recalled: note?.recalled ?? false,
108
+ });
109
+ }
110
+ //# sourceMappingURL=pack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack.js","sourceRoot":"","sources":["../../src/handoff/pack.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Atomic units of a transcript — the pieces the packer keeps or drops whole.
3
+ *
4
+ * An assistant message carrying `tool_calls` and the `tool` results that follow it form
5
+ * ONE unit: strict vendors reject a tool result whose call is missing, and a call whose
6
+ * results are missing. Every other message is a unit of its own. A stray `tool` result
7
+ * with no owning call joins the unit before it, so it can never lead a verbatim tail.
8
+ */
9
+ import type { HandoffMessage, TokenEstimator } from './types.js';
10
+ /** A contiguous, never-split run of messages: `messages = convo.slice(start, end)`. */
11
+ export interface Segment {
12
+ start: number;
13
+ end: number;
14
+ tokens: number;
15
+ }
16
+ /** Split `convo` into atomic units, in order. */
17
+ export declare function segmentTranscript(convo: readonly HandoffMessage[], estimate: TokenEstimator): Segment[];
18
+ //# sourceMappingURL=segments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"segments.d.ts","sourceRoot":"","sources":["../../src/handoff/segments.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,uFAAuF;AACvF,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,iDAAiD;AACjD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,EAAE,CAqBvG"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Atomic units of a transcript — the pieces the packer keeps or drops whole.
3
+ *
4
+ * An assistant message carrying `tool_calls` and the `tool` results that follow it form
5
+ * ONE unit: strict vendors reject a tool result whose call is missing, and a call whose
6
+ * results are missing. Every other message is a unit of its own. A stray `tool` result
7
+ * with no owning call joins the unit before it, so it can never lead a verbatim tail.
8
+ */
9
+ import { hasToolCalls, messageTokens } from './budget.js';
10
+ /** Split `convo` into atomic units, in order. */
11
+ export function segmentTranscript(convo, estimate) {
12
+ const out = [];
13
+ let i = 0;
14
+ while (i < convo.length) {
15
+ const m = convo[i];
16
+ let end = i + 1;
17
+ if (m.role === 'assistant' && hasToolCalls(m)) {
18
+ while (end < convo.length && convo[end].role === 'tool')
19
+ end += 1;
20
+ }
21
+ let tokens = 0;
22
+ for (let k = i; k < end; k++)
23
+ tokens += messageTokens(convo[k], estimate);
24
+ const prev = out[out.length - 1];
25
+ if (m.role === 'tool' && prev) {
26
+ prev.end = end;
27
+ prev.tokens += tokens;
28
+ }
29
+ else {
30
+ out.push({ start: i, end, tokens });
31
+ }
32
+ i = end;
33
+ }
34
+ return out;
35
+ }
36
+ //# sourceMappingURL=segments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"segments.js","sourceRoot":"","sources":["../../src/handoff/segments.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAU1D,iDAAiD;AACjD,MAAM,UAAU,iBAAiB,CAAC,KAAgC,EAAE,QAAwB;IAC1F,MAAM,GAAG,GAAc,EAAE,CAAC;IAC1B,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACpB,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QAChB,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9C,OAAO,GAAG,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,GAAG,CAAE,CAAC,IAAI,KAAK,MAAM;gBAAE,GAAG,IAAI,CAAC,CAAC;QACrE,CAAC;QACD,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE;YAAE,MAAM,IAAI,aAAa,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,QAAQ,CAAC,CAAC;QAC3E,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjC,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC;YAC9B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;YACf,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC;QACxB,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;QACtC,CAAC;QACD,CAAC,GAAG,GAAG,CAAC;IACV,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * The hand-off contract (PRD 34 §3.3) — the shapes a host passes in and gets back when
3
+ * a conversation moves from one model to another whose context window may be smaller.
4
+ * Pure data: no runtime imports, so the subpath stays Worker/browser/Node safe.
5
+ */
6
+ /** One chat message in the OpenAI-style shape every Builderforce host already speaks. */
7
+ export interface HandoffMessage {
8
+ role: 'system' | 'user' | 'assistant' | 'tool';
9
+ content: unknown;
10
+ tool_calls?: unknown;
11
+ tool_call_id?: string;
12
+ }
13
+ /** What {@link packHandoff} packs, and the budget it packs into. */
14
+ export interface HandoffPackInput {
15
+ messages: readonly HandoffMessage[];
16
+ /** The NEXT model's context window, in tokens. */
17
+ targetContextTokens: number;
18
+ /** Tokens held back for the next model's reply. */
19
+ reserveOutputTokens: number;
20
+ /** Tokens the advertised tool schemas cost. */
21
+ toolsTokens?: number;
22
+ /** Model refs, for the one-line hand-off note. */
23
+ from?: string;
24
+ to?: string;
25
+ /** Memory block for the latest directive, at most `budgetChars` long. */
26
+ recall?: (query: string, budgetChars: number) => Promise<string>;
27
+ /** Summary of the dropped middle. */
28
+ summarize?: (dropped: readonly HandoffMessage[]) => Promise<string>;
29
+ /** Token estimate for a text. Default: chars/4, rounded up. */
30
+ estimateTokens?: (text: string) => number;
31
+ /**
32
+ * Optional ceiling (ms) on each of `summarize` / `recall`. A call that outlives it is
33
+ * treated as a failure and packing falls through to the next note source. No ceiling
34
+ * when omitted.
35
+ */
36
+ timeoutMs?: number;
37
+ }
38
+ /** The packed transcript and how it was made. */
39
+ export interface HandoffPack {
40
+ messages: HandoffMessage[];
41
+ /** Messages from the input that are in neither the verbatim tail nor re-injected. */
42
+ dropped: number;
43
+ /** The note carries the `summarize` result. */
44
+ summarized: boolean;
45
+ /** The note carries the `recall` result. */
46
+ recalled: boolean;
47
+ estimatedTokens: number;
48
+ /** False only when the system message(s) + the latest directive alone exceed the budget. */
49
+ fits: boolean;
50
+ }
51
+ /** The three numbers the usable budget is computed from. */
52
+ export interface HandoffBudgetInput {
53
+ targetContextTokens: number;
54
+ reserveOutputTokens: number;
55
+ toolsTokens?: number;
56
+ }
57
+ /** A text → token-count estimator. */
58
+ export type TokenEstimator = (text: string) => number;
59
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/handoff/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,yFAAyF;AACzF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,oEAAoE;AACpE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IACpC,kDAAkD;IAClD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mDAAmD;IACnD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,yEAAyE;IACzE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACjE,qCAAqC;IACrC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpE,+DAA+D;IAC/D,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1C;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,iDAAiD;AACjD,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,qFAAqF;IACrF,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,UAAU,EAAE,OAAO,CAAC;IACpB,4CAA4C;IAC5C,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,4FAA4F;IAC5F,IAAI,EAAE,OAAO,CAAC;CACf;AAED,4DAA4D;AAC5D,MAAM,WAAW,kBAAkB;IACjC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,sCAAsC;AACtC,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The hand-off contract (PRD 34 §3.3) — the shapes a host passes in and gets back when
3
+ * a conversation moves from one model to another whose context window may be smaller.
4
+ * Pure data: no runtime imports, so the subpath stays Worker/browser/Node safe.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/handoff/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -37,8 +37,8 @@ export interface LimbicSessionOptions {
37
37
  */
38
38
  personalityTraits?: PersonalityTraits;
39
39
  /**
40
- * An explicit 8-dim resting setpoint (e.g. one already derived by the runtime's
41
- * `deriveLimbicSetpoints`). Overrides {@link personalityTraits}. Clamped to bounds.
40
+ * An explicit 8-dim resting setpoint (e.g. one already derived with the
41
+ * engine's `limbicSetpoints`). Overrides {@link personalityTraits}. Clamped to bounds.
42
42
  */
43
43
  personalitySetpoint?: ArrayLike<number>;
44
44
  }