mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
cf33309117
6 changed files with 75 additions and 35 deletions
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed mid-run threshold compaction silently skipping oversized trailing tool results ([#9740](https://github.com/earendil-works/pi/issues/9740)).
|
||||||
- Fixed signal-terminated local shell commands being reported as successful with partial output ([#9577](https://github.com/earendil-works/pi/issues/9577) by [@BrendanJMurphy](https://github.com/BrendanJMurphy)).
|
- Fixed signal-terminated local shell commands being reported as successful with partial output ([#9577](https://github.com/earendil-works/pi/issues/9577) by [@BrendanJMurphy](https://github.com/BrendanJMurphy)).
|
||||||
- Fixed local clipboard failures reporting success when the terminal ignored the fallback OSC 52 write, and added platform-specific setup guidance when no clipboard backend works ([#9618](https://github.com/earendil-works/pi/issues/9618)).
|
- Fixed local clipboard failures reporting success when the terminal ignored the fallback OSC 52 write, and added platform-specific setup guidance when no clipboard backend works ([#9618](https://github.com/earendil-works/pi/issues/9618)).
|
||||||
- Capped agent-level retry backoff at `retry.maxAgentDelayMs` (60s by default) so long retry runs stay responsive during prolonged transient outages ([#8826](https://github.com/earendil-works/pi/issues/8826)).
|
- Capped agent-level retry backoff at `retry.maxAgentDelayMs` (60s by default) so long retry runs stay responsive during prolonged transient outages ([#8826](https://github.com/earendil-works/pi/issues/8826)).
|
||||||
|
|
|
||||||
|
|
@ -442,13 +442,10 @@ export function findCutPoint(
|
||||||
|
|
||||||
// Check if we've exceeded the budget
|
// Check if we've exceeded the budget
|
||||||
if (accumulatedTokens >= keepRecentTokens) {
|
if (accumulatedTokens >= keepRecentTokens) {
|
||||||
// Find the closest valid cut point at or after this entry
|
// Prefer the closest valid cut point at or after this entry. If trailing
|
||||||
for (let c = 0; c < cutPoints.length; c++) {
|
// tool results exceed the budget by themselves, keep their preceding
|
||||||
if (cutPoints[c] >= i) {
|
// assistant tool call instead of falling back to the first message.
|
||||||
cutIndex = cutPoints[c];
|
cutIndex = cutPoints.find((candidate) => candidate >= i) ?? cutPoints[cutPoints.length - 1];
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -373,6 +373,42 @@ describe("findCutPoint", () => {
|
||||||
expect(customFitsBudget.isSplitTurn).toBe(false);
|
expect(customFitsBudget.isSplitTurn).toBe(false);
|
||||||
expect(customFitsBudget.turnStartIndex).toBe(-1);
|
expect(customFitsBudget.turnStartIndex).toBe(-1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Regression test for #9740.
|
||||||
|
it("should fall back to the latest valid cut point before oversized trailing tool results", () => {
|
||||||
|
const oldUser = createMessageEntry(createUserMessage("old history"));
|
||||||
|
const oldAssistant = createMessageEntry(createAssistantMessage("old answer"));
|
||||||
|
const currentUser = createMessageEntry(createUserMessage("read the large file"));
|
||||||
|
const toolCall = createMessageEntry({
|
||||||
|
...createAssistantMessage(""),
|
||||||
|
content: [{ type: "toolCall", id: "call-1", name: "read", arguments: { path: "big.txt" } }],
|
||||||
|
stopReason: "toolUse",
|
||||||
|
});
|
||||||
|
const toolResult = createMessageEntry({
|
||||||
|
role: "toolResult",
|
||||||
|
toolCallId: "call-1",
|
||||||
|
toolName: "read",
|
||||||
|
content: [{ type: "text", text: "x".repeat(8000) }],
|
||||||
|
isError: false,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
});
|
||||||
|
const entries = [oldUser, oldAssistant, currentUser, toolCall, toolResult];
|
||||||
|
|
||||||
|
const result = findCutPoint(entries, 0, entries.length, 1000);
|
||||||
|
expect(result).toEqual({
|
||||||
|
firstKeptEntryIndex: 3,
|
||||||
|
turnStartIndex: 2,
|
||||||
|
isSplitTurn: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const preparation = prepareCompaction(entries, {
|
||||||
|
...DEFAULT_COMPACTION_SETTINGS,
|
||||||
|
keepRecentTokens: 1000,
|
||||||
|
});
|
||||||
|
expect(preparation?.firstKeptEntryId).toBe(toolCall.id);
|
||||||
|
expect(preparation?.messagesToSummarize).toEqual([oldUser.message, oldAssistant.message]);
|
||||||
|
expect(preparation?.turnPrefixMessages).toEqual([currentUser.message]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("buildSessionContext", () => {
|
describe("buildSessionContext", () => {
|
||||||
|
|
|
||||||
|
|
@ -471,10 +471,11 @@ describe("AgentSession compaction characterization", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Regression coverage for #8133: model overrides must also apply between assistant turns.
|
// Regression coverage for #8133: model overrides must also apply between assistant turns.
|
||||||
|
// Regression coverage for #9740: an oversized trailing tool result must still produce a cut point.
|
||||||
it.each([false, true])(
|
it.each([false, true])(
|
||||||
"compacts after a tool result in the same run (model override: %s)",
|
"compacts after an oversized tool result in the same run (model override: %s)",
|
||||||
async (modelOverride) => {
|
async (modelOverride) => {
|
||||||
const toolResult = `large-tool-result:${"x".repeat(6800)}`;
|
const toolResult = `large-tool-result:${"x".repeat(8000)}`;
|
||||||
const largeTool: AgentTool = {
|
const largeTool: AgentTool = {
|
||||||
name: "large_result",
|
name: "large_result",
|
||||||
label: "Large result",
|
label: "Large result",
|
||||||
|
|
@ -532,8 +533,8 @@ describe("AgentSession compaction characterization", () => {
|
||||||
const agentStartsBefore = harness.eventsOfType("agent_start").length;
|
const agentStartsBefore = harness.eventsOfType("agent_start").length;
|
||||||
await harness.session.prompt("run the large tool");
|
await harness.session.prompt("run the large tool");
|
||||||
|
|
||||||
expect(order).toEqual(["compaction", "provider"]);
|
expect(order.slice(0, 2)).toEqual(["compaction", "provider"]);
|
||||||
expect(observedSettings).toEqual([{ enabled: true, reserveTokens: 400, keepRecentTokens: 1750 }]);
|
expect(observedSettings[0]).toEqual({ enabled: true, reserveTokens: 400, keepRecentTokens: 1750 });
|
||||||
expect(harness.eventsOfType("agent_start")).toHaveLength(agentStartsBefore + 1);
|
expect(harness.eventsOfType("agent_start")).toHaveLength(agentStartsBefore + 1);
|
||||||
expect(harness.eventsOfType("compaction_start").at(-1)).toEqual({
|
expect(harness.eventsOfType("compaction_start").at(-1)).toEqual({
|
||||||
type: "compaction_start",
|
type: "compaction_start",
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Reduced fuzzy search latency for long texts by using native substring search instead of scanning each character in JavaScript ([#9267](https://github.com/earendil-works/pi/issues/9267)).
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed fullscreen clipboard failures hiding actionable backend error messages behind a generic notice, and extended failure notices to five seconds ([#9618](https://github.com/earendil-works/pi/issues/9618)).
|
- Fixed fullscreen clipboard failures hiding actionable backend error messages behind a generic notice, and extended failure notices to five seconds ([#9618](https://github.com/earendil-works/pi/issues/9618)).
|
||||||
|
|
|
||||||
|
|
@ -27,33 +27,34 @@ export function fuzzyMatch(query: string, text: string): FuzzyMatch {
|
||||||
let lastMatchIndex = -1;
|
let lastMatchIndex = -1;
|
||||||
let consecutiveMatches = 0;
|
let consecutiveMatches = 0;
|
||||||
|
|
||||||
for (let i = 0; i < textLower.length && queryIndex < normalizedQuery.length; i++) {
|
while (queryIndex < normalizedQuery.length) {
|
||||||
if (textLower[i] === normalizedQuery[queryIndex]) {
|
const i = textLower.indexOf(normalizedQuery[queryIndex]!, lastMatchIndex + 1);
|
||||||
const isWordBoundary = i === 0 || /[\s\-_./:]/.test(textLower[i - 1]!);
|
if (i === -1) break;
|
||||||
|
|
||||||
// Reward consecutive matches
|
const isWordBoundary = i === 0 || /[\s\-_./:]/.test(textLower[i - 1]!);
|
||||||
if (lastMatchIndex === i - 1) {
|
|
||||||
consecutiveMatches++;
|
// Reward consecutive matches
|
||||||
score -= consecutiveMatches * 5;
|
if (lastMatchIndex === i - 1) {
|
||||||
} else {
|
consecutiveMatches++;
|
||||||
consecutiveMatches = 0;
|
score -= consecutiveMatches * 5;
|
||||||
// Penalize gaps
|
} else {
|
||||||
if (lastMatchIndex >= 0) {
|
consecutiveMatches = 0;
|
||||||
score += (i - lastMatchIndex - 1) * 2;
|
// Penalize gaps
|
||||||
}
|
if (lastMatchIndex >= 0) {
|
||||||
|
score += (i - lastMatchIndex - 1) * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reward word boundary matches
|
|
||||||
if (isWordBoundary) {
|
|
||||||
score -= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slight penalty for later matches
|
|
||||||
score += i * 0.1;
|
|
||||||
|
|
||||||
lastMatchIndex = i;
|
|
||||||
queryIndex++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reward word boundary matches
|
||||||
|
if (isWordBoundary) {
|
||||||
|
score -= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slight penalty for later matches
|
||||||
|
score += i * 0.1;
|
||||||
|
|
||||||
|
lastMatchIndex = i;
|
||||||
|
queryIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queryIndex < normalizedQuery.length) {
|
if (queryIndex < normalizedQuery.length) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue