@mnemonik/shared 7.51.0 → 7.55.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/dist/contextBudget.d.ts +8 -3
- package/dist/contextBudget.d.ts.map +1 -1
- package/dist/contextBudget.js +19 -94
- package/dist/contextBudget.js.map +1 -1
- package/package.json +1 -1
- package/src/contextBudget.ts +24 -103
package/dist/contextBudget.d.ts
CHANGED
|
@@ -3,26 +3,31 @@ export type ContextBudgetUnits = 'characters' | 'utf8-bytes';
|
|
|
3
3
|
export interface ContextBudgetItem {
|
|
4
4
|
/** Exact text emitted when the item fits in full. */
|
|
5
5
|
text: string;
|
|
6
|
-
/** Stable envelope name used
|
|
6
|
+
/** Stable envelope name used for diagnostics only. Never emitted as a retrieval stub. */
|
|
7
7
|
name: string;
|
|
8
8
|
/** Memory identity when this item is one pointer-terminated memory precis. */
|
|
9
9
|
memoryId?: string;
|
|
10
10
|
/** Memory identities when one rendered envelope represents several memories. */
|
|
11
11
|
memoryIds?: readonly string[];
|
|
12
|
+
/** Exact server-side call that can retrieve this emission after it is shed. */
|
|
13
|
+
retrievalRoute?: string;
|
|
12
14
|
}
|
|
13
15
|
export type ContextBudgetPiece = string | ContextBudgetItem;
|
|
14
16
|
export interface BudgetedContextFit {
|
|
15
17
|
value: string;
|
|
16
18
|
fullyDeliveredItems: number;
|
|
19
|
+
/** Retained for caller compatibility. Atomic shedding never sets it. */
|
|
17
20
|
partialItemIndex: number | null;
|
|
21
|
+
/** Retained for caller compatibility. Atomic shedding always returns zero. */
|
|
18
22
|
partialUnitsDelivered: number;
|
|
19
23
|
}
|
|
20
24
|
/** Measure rendered context in the host's native budget unit. */
|
|
21
25
|
export declare function measureContext(text: string, units?: ContextBudgetUnits): number;
|
|
22
26
|
/**
|
|
23
27
|
* Compose ordered items within a fixed budget. Full items are emitted exactly;
|
|
24
|
-
* the first item that does not fit
|
|
25
|
-
*
|
|
28
|
+
* the first item that does not fit and every later item are shed. Shed items
|
|
29
|
+
* share one trailing marker containing only their explicit retrieval routes.
|
|
30
|
+
* Items without a route leave no agent-visible stub.
|
|
26
31
|
*/
|
|
27
32
|
export declare function fitContextItems(items: readonly ContextBudgetItem[], opts: {
|
|
28
33
|
budget: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextBudget.d.ts","sourceRoot":"","sources":["../src/contextBudget.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAElE,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG,YAAY,CAAC;AAE7D,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,
|
|
1
|
+
{"version":3,"file":"contextBudget.d.ts","sourceRoot":"","sources":["../src/contextBudget.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAElE,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG,YAAY,CAAC;AAE7D,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,+EAA+E;IAC/E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,iBAAiB,CAAC;AAE5D,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,mBAAmB,EAAE,MAAM,CAAC;IAC5B,wEAAwE;IACxE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,8EAA8E;IAC9E,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AA6BD,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,kBAAiC,GAAG,MAAM,CAE7F;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,iBAAiB,EAAE,EACnC,IAAI,EAAE;IACJ,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,cAAc,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAC/C,GACA,kBAAkB,CAiDpB;AA4BD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,SAAS,GAAE,SAAS,MAAM,EAAO,GAChC,iBAAiB,EAAE,CAMrB;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAClF,MAAM,CAIR;AAED,iEAAiE;AACjE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAExD"}
|
package/dist/contextBudget.js
CHANGED
|
@@ -1,26 +1,24 @@
|
|
|
1
1
|
import { AMBIENT_ITEM_POINTER_SOURCE } from './ambientPointer.js';
|
|
2
2
|
export { AMBIENT_ITEM_POINTER_SOURCE } from './ambientPointer.js';
|
|
3
3
|
const JOINER = '\n\n';
|
|
4
|
-
const CONTINUATION_PREFIX = '… continues: ';
|
|
5
4
|
const NOT_REACHED_PREFIX = '… not reached: ';
|
|
6
5
|
function itemMemoryIds(item) {
|
|
7
6
|
if (item.memoryIds && item.memoryIds.length > 0)
|
|
8
7
|
return item.memoryIds;
|
|
9
8
|
return item.memoryId ? [item.memoryId] : [];
|
|
10
9
|
}
|
|
11
|
-
function
|
|
10
|
+
function retrievalRoutes(item) {
|
|
12
11
|
const memoryIds = itemMemoryIds(item);
|
|
13
12
|
if (memoryIds.length > 0)
|
|
14
|
-
return memoryIds.map((id) => `memory_get ${id}`)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
function continuationPointer(item) {
|
|
18
|
-
return `${CONTINUATION_PREFIX}${identity(item)}`;
|
|
13
|
+
return memoryIds.map((id) => `memory_get ${id}`);
|
|
14
|
+
const route = item.retrievalRoute?.replace(/\s+/g, ' ').trim();
|
|
15
|
+
return route ? [route] : [];
|
|
19
16
|
}
|
|
20
17
|
function notReachedPointer(items) {
|
|
21
|
-
|
|
18
|
+
const routes = [...new Set(items.flatMap(retrievalRoutes))];
|
|
19
|
+
if (routes.length === 0)
|
|
22
20
|
return null;
|
|
23
|
-
return `${NOT_REACHED_PREFIX}${
|
|
21
|
+
return `${NOT_REACHED_PREFIX}${routes.join(', ')}`;
|
|
24
22
|
}
|
|
25
23
|
function joinSections(sections) {
|
|
26
24
|
return sections
|
|
@@ -31,66 +29,11 @@ function joinSections(sections) {
|
|
|
31
29
|
export function measureContext(text, units = 'characters') {
|
|
32
30
|
return units === 'utf8-bytes' ? Buffer.byteLength(text, 'utf8') : text.length;
|
|
33
31
|
}
|
|
34
|
-
function withoutTerminalMemoryPointer(item) {
|
|
35
|
-
if (!item.memoryId)
|
|
36
|
-
return item.text;
|
|
37
|
-
const terminalPointer = new RegExp(`\\s*${AMBIENT_ITEM_POINTER_SOURCE}\\s*$`);
|
|
38
|
-
return item.text.replace(terminalPointer, '').trimEnd();
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Prefer bullet lines whenever the item contains them. Text ahead of the first
|
|
42
|
-
* bullet is framing and travels with the first admitted fact. Otherwise each
|
|
43
|
-
* sentence (including a final sentence ending at end-of-item) is one unit.
|
|
44
|
-
*/
|
|
45
|
-
function naturalUnits(item) {
|
|
46
|
-
const text = withoutTerminalMemoryPointer(item);
|
|
47
|
-
const bulletStarts = [];
|
|
48
|
-
const bulletPattern = /(^|\n)(?=[ \t]*(?:•|[-*])[ \t]+)/g;
|
|
49
|
-
let bulletMatch;
|
|
50
|
-
while ((bulletMatch = bulletPattern.exec(text)) !== null) {
|
|
51
|
-
bulletStarts.push(bulletMatch.index + (bulletMatch[1] ?? '').length);
|
|
52
|
-
if (bulletPattern.lastIndex === bulletMatch.index)
|
|
53
|
-
bulletPattern.lastIndex += 1;
|
|
54
|
-
}
|
|
55
|
-
// The validated-use clause is a factual unit of its own. Keeping its start
|
|
56
|
-
// beside bullet starts means a partial-memory fit emits the whole clause or
|
|
57
|
-
// none of it; the context boundary can never cut inside the count or date.
|
|
58
|
-
const trackStarts = [];
|
|
59
|
-
const trackPattern = /(^|\n)(?=\(acted on and validated \d+ times; last \d{4}-\d{2}-\d{2}\))/g;
|
|
60
|
-
let trackMatch;
|
|
61
|
-
while ((trackMatch = trackPattern.exec(text)) !== null) {
|
|
62
|
-
trackStarts.push(trackMatch.index + (trackMatch[1] ?? '').length);
|
|
63
|
-
if (trackPattern.lastIndex === trackMatch.index)
|
|
64
|
-
trackPattern.lastIndex += 1;
|
|
65
|
-
}
|
|
66
|
-
const explicitStarts = [...new Set([...bulletStarts, ...trackStarts])].sort((a, b) => a - b);
|
|
67
|
-
if (explicitStarts.length > 0) {
|
|
68
|
-
return {
|
|
69
|
-
prefix: text.slice(0, explicitStarts[0]),
|
|
70
|
-
units: explicitStarts.map((start, index) => text.slice(start, explicitStarts[index + 1])),
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
const units = [];
|
|
74
|
-
const sentenceEnd = /[.!?](?:["')\]]*)?(?=\s|$)/g;
|
|
75
|
-
let cursor = 0;
|
|
76
|
-
let sentenceMatch;
|
|
77
|
-
while ((sentenceMatch = sentenceEnd.exec(text)) !== null) {
|
|
78
|
-
const end = sentenceMatch.index + sentenceMatch[0].length;
|
|
79
|
-
units.push(text.slice(cursor, end));
|
|
80
|
-
cursor = end;
|
|
81
|
-
}
|
|
82
|
-
if (cursor < text.length)
|
|
83
|
-
units.push(text.slice(cursor));
|
|
84
|
-
return { prefix: '', units: units.filter((unit) => unit.trim().length > 0) };
|
|
85
|
-
}
|
|
86
|
-
function partialText(item, plan, count) {
|
|
87
|
-
const content = `${plan.prefix}${plan.units.slice(0, count).join('')}`.trimEnd();
|
|
88
|
-
return `${content}\n${continuationPointer(item)}`;
|
|
89
|
-
}
|
|
90
32
|
/**
|
|
91
33
|
* Compose ordered items within a fixed budget. Full items are emitted exactly;
|
|
92
|
-
* the first item that does not fit
|
|
93
|
-
*
|
|
34
|
+
* the first item that does not fit and every later item are shed. Shed items
|
|
35
|
+
* share one trailing marker containing only their explicit retrieval routes.
|
|
36
|
+
* Items without a route leave no agent-visible stub.
|
|
94
37
|
*/
|
|
95
38
|
export function fitContextItems(items, opts) {
|
|
96
39
|
const units = opts.units ?? 'characters';
|
|
@@ -120,35 +63,17 @@ export function fitContextItems(items, opts) {
|
|
|
120
63
|
accepted.push(item.text);
|
|
121
64
|
continue;
|
|
122
65
|
}
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
...suffix.map((entry) => entry.text),
|
|
130
|
-
notReachedPointer(later),
|
|
131
|
-
]);
|
|
132
|
-
if (measureContext(candidate, units) > opts.budget)
|
|
133
|
-
break;
|
|
134
|
-
partialUnitsDelivered = count;
|
|
135
|
-
}
|
|
136
|
-
// A one-fact excerpt reads as though it were the whole memory. Require two
|
|
137
|
-
// complete units before admitting a partial item; otherwise name it only.
|
|
138
|
-
const meaningfulMinimum = Math.min(2, plan.units.length);
|
|
139
|
-
const partialAccepted = partialUnitsDelivered >= meaningfulMinimum && partialUnitsDelivered > 0;
|
|
140
|
-
const partial = partialAccepted ? partialText(item, plan, partialUnitsDelivered) : null;
|
|
141
|
-
const unreached = partialAccepted ? later : items.slice(index);
|
|
66
|
+
const withoutPointer = joinSections([...accepted, ...suffix.map((entry) => entry.text)]);
|
|
67
|
+
const withPointer = joinSections([
|
|
68
|
+
...accepted,
|
|
69
|
+
...suffix.map((entry) => entry.text),
|
|
70
|
+
notReachedPointer(items.slice(index)),
|
|
71
|
+
]);
|
|
142
72
|
return {
|
|
143
|
-
value:
|
|
144
|
-
...accepted,
|
|
145
|
-
partial,
|
|
146
|
-
...suffix.map((entry) => entry.text),
|
|
147
|
-
notReachedPointer(unreached),
|
|
148
|
-
]),
|
|
73
|
+
value: measureContext(withPointer, units) <= opts.budget ? withPointer : withoutPointer,
|
|
149
74
|
fullyDeliveredItems: index,
|
|
150
|
-
partialItemIndex:
|
|
151
|
-
partialUnitsDelivered:
|
|
75
|
+
partialItemIndex: null,
|
|
76
|
+
partialUnitsDelivered: 0,
|
|
152
77
|
};
|
|
153
78
|
}
|
|
154
79
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextBudget.js","sourceRoot":"","sources":["../src/contextBudget.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAElE,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"contextBudget.js","sourceRoot":"","sources":["../src/contextBudget.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/contextBudget.ts
CHANGED
|
@@ -7,12 +7,14 @@ export type ContextBudgetUnits = 'characters' | 'utf8-bytes';
|
|
|
7
7
|
export interface ContextBudgetItem {
|
|
8
8
|
/** Exact text emitted when the item fits in full. */
|
|
9
9
|
text: string;
|
|
10
|
-
/** Stable envelope name used
|
|
10
|
+
/** Stable envelope name used for diagnostics only. Never emitted as a retrieval stub. */
|
|
11
11
|
name: string;
|
|
12
12
|
/** Memory identity when this item is one pointer-terminated memory precis. */
|
|
13
13
|
memoryId?: string;
|
|
14
14
|
/** Memory identities when one rendered envelope represents several memories. */
|
|
15
15
|
memoryIds?: readonly string[];
|
|
16
|
+
/** Exact server-side call that can retrieve this emission after it is shed. */
|
|
17
|
+
retrievalRoute?: string;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export type ContextBudgetPiece = string | ContextBudgetItem;
|
|
@@ -20,12 +22,13 @@ export type ContextBudgetPiece = string | ContextBudgetItem;
|
|
|
20
22
|
export interface BudgetedContextFit {
|
|
21
23
|
value: string;
|
|
22
24
|
fullyDeliveredItems: number;
|
|
25
|
+
/** Retained for caller compatibility. Atomic shedding never sets it. */
|
|
23
26
|
partialItemIndex: number | null;
|
|
27
|
+
/** Retained for caller compatibility. Atomic shedding always returns zero. */
|
|
24
28
|
partialUnitsDelivered: number;
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
const JOINER = '\n\n';
|
|
28
|
-
const CONTINUATION_PREFIX = '… continues: ';
|
|
29
32
|
const NOT_REACHED_PREFIX = '… not reached: ';
|
|
30
33
|
|
|
31
34
|
function itemMemoryIds(item: ContextBudgetItem): readonly string[] {
|
|
@@ -33,19 +36,17 @@ function itemMemoryIds(item: ContextBudgetItem): readonly string[] {
|
|
|
33
36
|
return item.memoryId ? [item.memoryId] : [];
|
|
34
37
|
}
|
|
35
38
|
|
|
36
|
-
function
|
|
39
|
+
function retrievalRoutes(item: ContextBudgetItem): readonly string[] {
|
|
37
40
|
const memoryIds = itemMemoryIds(item);
|
|
38
|
-
if (memoryIds.length > 0) return memoryIds.map((id) => `memory_get ${id}`)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
function continuationPointer(item: ContextBudgetItem): string {
|
|
43
|
-
return `${CONTINUATION_PREFIX}${identity(item)}`;
|
|
41
|
+
if (memoryIds.length > 0) return memoryIds.map((id) => `memory_get ${id}`);
|
|
42
|
+
const route = item.retrievalRoute?.replace(/\s+/g, ' ').trim();
|
|
43
|
+
return route ? [route] : [];
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
function notReachedPointer(items: readonly ContextBudgetItem[]): string | null {
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
const routes = [...new Set(items.flatMap(retrievalRoutes))];
|
|
48
|
+
if (routes.length === 0) return null;
|
|
49
|
+
return `${NOT_REACHED_PREFIX}${routes.join(', ')}`;
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
function joinSections(sections: readonly (string | null)[]): string {
|
|
@@ -59,73 +60,11 @@ export function measureContext(text: string, units: ContextBudgetUnits = 'charac
|
|
|
59
60
|
return units === 'utf8-bytes' ? Buffer.byteLength(text, 'utf8') : text.length;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
interface NaturalUnits {
|
|
63
|
-
prefix: string;
|
|
64
|
-
units: string[];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function withoutTerminalMemoryPointer(item: ContextBudgetItem): string {
|
|
68
|
-
if (!item.memoryId) return item.text;
|
|
69
|
-
const terminalPointer = new RegExp(`\\s*${AMBIENT_ITEM_POINTER_SOURCE}\\s*$`);
|
|
70
|
-
return item.text.replace(terminalPointer, '').trimEnd();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Prefer bullet lines whenever the item contains them. Text ahead of the first
|
|
75
|
-
* bullet is framing and travels with the first admitted fact. Otherwise each
|
|
76
|
-
* sentence (including a final sentence ending at end-of-item) is one unit.
|
|
77
|
-
*/
|
|
78
|
-
function naturalUnits(item: ContextBudgetItem): NaturalUnits {
|
|
79
|
-
const text = withoutTerminalMemoryPointer(item);
|
|
80
|
-
const bulletStarts: number[] = [];
|
|
81
|
-
const bulletPattern = /(^|\n)(?=[ \t]*(?:•|[-*])[ \t]+)/g;
|
|
82
|
-
let bulletMatch: RegExpExecArray | null;
|
|
83
|
-
while ((bulletMatch = bulletPattern.exec(text)) !== null) {
|
|
84
|
-
bulletStarts.push(bulletMatch.index + (bulletMatch[1] ?? '').length);
|
|
85
|
-
if (bulletPattern.lastIndex === bulletMatch.index) bulletPattern.lastIndex += 1;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// The validated-use clause is a factual unit of its own. Keeping its start
|
|
89
|
-
// beside bullet starts means a partial-memory fit emits the whole clause or
|
|
90
|
-
// none of it; the context boundary can never cut inside the count or date.
|
|
91
|
-
const trackStarts: number[] = [];
|
|
92
|
-
const trackPattern = /(^|\n)(?=\(acted on and validated \d+ times; last \d{4}-\d{2}-\d{2}\))/g;
|
|
93
|
-
let trackMatch: RegExpExecArray | null;
|
|
94
|
-
while ((trackMatch = trackPattern.exec(text)) !== null) {
|
|
95
|
-
trackStarts.push(trackMatch.index + (trackMatch[1] ?? '').length);
|
|
96
|
-
if (trackPattern.lastIndex === trackMatch.index) trackPattern.lastIndex += 1;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const explicitStarts = [...new Set([...bulletStarts, ...trackStarts])].sort((a, b) => a - b);
|
|
100
|
-
if (explicitStarts.length > 0) {
|
|
101
|
-
return {
|
|
102
|
-
prefix: text.slice(0, explicitStarts[0]),
|
|
103
|
-
units: explicitStarts.map((start, index) => text.slice(start, explicitStarts[index + 1])),
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const units: string[] = [];
|
|
108
|
-
const sentenceEnd = /[.!?](?:["')\]]*)?(?=\s|$)/g;
|
|
109
|
-
let cursor = 0;
|
|
110
|
-
let sentenceMatch: RegExpExecArray | null;
|
|
111
|
-
while ((sentenceMatch = sentenceEnd.exec(text)) !== null) {
|
|
112
|
-
const end = sentenceMatch.index + sentenceMatch[0].length;
|
|
113
|
-
units.push(text.slice(cursor, end));
|
|
114
|
-
cursor = end;
|
|
115
|
-
}
|
|
116
|
-
if (cursor < text.length) units.push(text.slice(cursor));
|
|
117
|
-
return { prefix: '', units: units.filter((unit) => unit.trim().length > 0) };
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function partialText(item: ContextBudgetItem, plan: NaturalUnits, count: number): string {
|
|
121
|
-
const content = `${plan.prefix}${plan.units.slice(0, count).join('')}`.trimEnd();
|
|
122
|
-
return `${content}\n${continuationPointer(item)}`;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
63
|
/**
|
|
126
64
|
* Compose ordered items within a fixed budget. Full items are emitted exactly;
|
|
127
|
-
* the first item that does not fit
|
|
128
|
-
*
|
|
65
|
+
* the first item that does not fit and every later item are shed. Shed items
|
|
66
|
+
* share one trailing marker containing only their explicit retrieval routes.
|
|
67
|
+
* Items without a route leave no agent-visible stub.
|
|
129
68
|
*/
|
|
130
69
|
export function fitContextItems(
|
|
131
70
|
items: readonly ContextBudgetItem[],
|
|
@@ -163,35 +102,17 @@ export function fitContextItems(
|
|
|
163
102
|
continue;
|
|
164
103
|
}
|
|
165
104
|
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
...suffix.map((entry) => entry.text),
|
|
173
|
-
notReachedPointer(later),
|
|
174
|
-
]);
|
|
175
|
-
if (measureContext(candidate, units) > opts.budget) break;
|
|
176
|
-
partialUnitsDelivered = count;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// A one-fact excerpt reads as though it were the whole memory. Require two
|
|
180
|
-
// complete units before admitting a partial item; otherwise name it only.
|
|
181
|
-
const meaningfulMinimum = Math.min(2, plan.units.length);
|
|
182
|
-
const partialAccepted = partialUnitsDelivered >= meaningfulMinimum && partialUnitsDelivered > 0;
|
|
183
|
-
const partial = partialAccepted ? partialText(item, plan, partialUnitsDelivered) : null;
|
|
184
|
-
const unreached = partialAccepted ? later : items.slice(index);
|
|
105
|
+
const withoutPointer = joinSections([...accepted, ...suffix.map((entry) => entry.text)]);
|
|
106
|
+
const withPointer = joinSections([
|
|
107
|
+
...accepted,
|
|
108
|
+
...suffix.map((entry) => entry.text),
|
|
109
|
+
notReachedPointer(items.slice(index)),
|
|
110
|
+
]);
|
|
185
111
|
return {
|
|
186
|
-
value:
|
|
187
|
-
...accepted,
|
|
188
|
-
partial,
|
|
189
|
-
...suffix.map((entry) => entry.text),
|
|
190
|
-
notReachedPointer(unreached),
|
|
191
|
-
]),
|
|
112
|
+
value: measureContext(withPointer, units) <= opts.budget ? withPointer : withoutPointer,
|
|
192
113
|
fullyDeliveredItems: index,
|
|
193
|
-
partialItemIndex:
|
|
194
|
-
partialUnitsDelivered:
|
|
114
|
+
partialItemIndex: null,
|
|
115
|
+
partialUnitsDelivered: 0,
|
|
195
116
|
};
|
|
196
117
|
}
|
|
197
118
|
|