opencode-lore 0.2.1 → 0.2.2
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/package.json +1 -1
- package/src/gradient.ts +25 -6
- package/src/index.ts +3 -2
package/package.json
CHANGED
package/src/gradient.ts
CHANGED
|
@@ -722,8 +722,27 @@ export function transform(input: {
|
|
|
722
722
|
const maxInput = contextLimit - outputReserved;
|
|
723
723
|
const sid = input.sessionID ?? input.messages[0]?.info.sessionID;
|
|
724
724
|
|
|
725
|
+
// True when we have real API token data from a previous turn in this session.
|
|
726
|
+
// When false (first turn / session change), chars/4 estimates can undercount by
|
|
727
|
+
// up to 1.8x — so tryFit output must be validated with a safety multiplier before
|
|
728
|
+
// being used, to prevent sending an apparently-fitting window that actually overflows.
|
|
729
|
+
const calibrated = lastKnownInput > 0 && sid === lastKnownSessionID;
|
|
730
|
+
|
|
731
|
+
// On uncalibrated turns, apply this multiplier to tryFit's estimated total to
|
|
732
|
+
// approximate the real token count. 1.5 is conservative but not so aggressive
|
|
733
|
+
// that it forces layer 4 on modestly-sized sessions.
|
|
734
|
+
const UNCALIBRATED_SAFETY = 1.5;
|
|
735
|
+
|
|
736
|
+
// Returns true if the tryFit result is safe to use: either we have calibrated
|
|
737
|
+
// data (exact) or the estimated total * safety factor fits within maxInput.
|
|
738
|
+
function fitsWithSafetyMargin(result: { totalTokens: number } | null): boolean {
|
|
739
|
+
if (!result) return false;
|
|
740
|
+
if (calibrated) return true;
|
|
741
|
+
return result.totalTokens * UNCALIBRATED_SAFETY <= maxInput;
|
|
742
|
+
}
|
|
743
|
+
|
|
725
744
|
let expectedInput: number;
|
|
726
|
-
if (
|
|
745
|
+
if (calibrated) {
|
|
727
746
|
// Exact approach: prior API count + estimate of only the new messages.
|
|
728
747
|
const newMsgCount = Math.max(0, input.messages.length - lastKnownMessageCount);
|
|
729
748
|
const newMsgTokens = newMsgCount > 0
|
|
@@ -793,7 +812,7 @@ export function transform(input: {
|
|
|
793
812
|
rawBudget,
|
|
794
813
|
strip: "none",
|
|
795
814
|
});
|
|
796
|
-
if (layer1) return { ...layer1
|
|
815
|
+
if (fitsWithSafetyMargin(layer1)) return { ...layer1!, layer: 1, usable, distilledBudget, rawBudget };
|
|
797
816
|
}
|
|
798
817
|
|
|
799
818
|
// Layer 1 didn't fit (or was force-skipped) — reset the raw window cache.
|
|
@@ -812,9 +831,9 @@ export function transform(input: {
|
|
|
812
831
|
strip: "old-tools",
|
|
813
832
|
protectedTurns: 2,
|
|
814
833
|
});
|
|
815
|
-
if (layer2) {
|
|
834
|
+
if (fitsWithSafetyMargin(layer2)) {
|
|
816
835
|
urgentDistillation = true;
|
|
817
|
-
return { ...layer2
|
|
836
|
+
return { ...layer2!, layer: 2, usable, distilledBudget, rawBudget };
|
|
818
837
|
}
|
|
819
838
|
}
|
|
820
839
|
|
|
@@ -833,9 +852,9 @@ export function transform(input: {
|
|
|
833
852
|
rawBudget: Math.floor(usable * 0.55),
|
|
834
853
|
strip: "all-tools",
|
|
835
854
|
});
|
|
836
|
-
if (layer3) {
|
|
855
|
+
if (fitsWithSafetyMargin(layer3)) {
|
|
837
856
|
urgentDistillation = true;
|
|
838
|
-
return { ...layer3
|
|
857
|
+
return { ...layer3!, layer: 3, usable, distilledBudget, rawBudget };
|
|
839
858
|
}
|
|
840
859
|
|
|
841
860
|
// Layer 4: Emergency — last 2 distillations, last 3 raw messages with tool parts intact.
|
package/src/index.ts
CHANGED
|
@@ -389,12 +389,13 @@ export const LorePlugin: Plugin = async (ctx) => {
|
|
|
389
389
|
// Layer 0 means all messages fit within the context budget — leave them alone
|
|
390
390
|
// so the append-only sequence stays intact for prompt caching.
|
|
391
391
|
if (result.layer > 0) {
|
|
392
|
+
// The API requires the conversation to end with a user message.
|
|
393
|
+
// Always drop trailing non-user messages — even assistant messages with
|
|
394
|
+
// tool parts. A hard API error is worse than the model re-invoking a tool.
|
|
392
395
|
while (
|
|
393
396
|
result.messages.length > 0 &&
|
|
394
397
|
result.messages.at(-1)!.info.role !== "user"
|
|
395
398
|
) {
|
|
396
|
-
const last = result.messages.at(-1)!;
|
|
397
|
-
if (last.parts.some((p) => p.type === "tool")) break;
|
|
398
399
|
const dropped = result.messages.pop()!;
|
|
399
400
|
console.error(
|
|
400
401
|
"[lore] WARN: dropping trailing",
|