@astrosheep/pi-context 0.25.1 → 0.25.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/dist/build-info.json
CHANGED
package/dist/extension.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// <define:__PI_CONTEXT_BUILD__>
|
|
2
|
-
var define_PI_CONTEXT_BUILD_default = { version: "0.25.
|
|
2
|
+
var define_PI_CONTEXT_BUILD_default = { version: "0.25.2", sourceHash: "e240c1946d0f2b6d7b1d83ed658c348c7daed5fce5a374844215f1eebddcb25f" };
|
|
3
3
|
|
|
4
4
|
// src/index.ts
|
|
5
5
|
import { VERSION as VERSION2 } from "@earendil-works/pi-coding-agent";
|
|
@@ -1389,6 +1389,7 @@ function registerBudget(pi, isEnabled, settingsManager) {
|
|
|
1389
1389
|
const invalidateThresholds = () => {
|
|
1390
1390
|
cachedPolicy = void 0;
|
|
1391
1391
|
};
|
|
1392
|
+
const formatRemaining = (remaining) => `${Math.max(0, Math.ceil(remaining / 1e3))}k`;
|
|
1392
1393
|
let pendingGuidance;
|
|
1393
1394
|
let pendingWarning;
|
|
1394
1395
|
let pendingNotices = [];
|
|
@@ -1396,7 +1397,7 @@ function registerBudget(pi, isEnabled, settingsManager) {
|
|
|
1396
1397
|
const windowId = currentWindowId(ctx);
|
|
1397
1398
|
for (const notice of pendingNotices) {
|
|
1398
1399
|
if (notice.windowId !== windowId || !hasWindowMessage(ctx, notice.customType)) continue;
|
|
1399
|
-
ctx.ui.notify(notice.customType === WARNING_TYPE ?
|
|
1400
|
+
ctx.ui.notify(notice.customType === WARNING_TYPE ? `pi-context: Context almost full \u2014 ${formatRemaining(notice.remaining)} remaining` : `pi-context: Context running low \u2014 ${formatRemaining(notice.remaining)} remaining`, "warning");
|
|
1400
1401
|
}
|
|
1401
1402
|
pendingNotices = [];
|
|
1402
1403
|
};
|
|
@@ -1418,7 +1419,7 @@ function registerBudget(pi, isEnabled, settingsManager) {
|
|
|
1418
1419
|
clearStaged();
|
|
1419
1420
|
const windowId = currentWindowId(ctx);
|
|
1420
1421
|
const drafts = staged.filter((draft) => draft !== void 0 && draft.windowId === windowId);
|
|
1421
|
-
pendingNotices = drafts.map(({ windowId: windowId2, customType }) => ({ windowId: windowId2, customType }));
|
|
1422
|
+
pendingNotices = drafts.map(({ windowId: windowId2, customType, remaining }) => ({ windowId: windowId2, customType, remaining }));
|
|
1422
1423
|
return drafts.map((draft) => ({
|
|
1423
1424
|
type: "custom_message",
|
|
1424
1425
|
customType: draft.customType,
|
|
@@ -1433,7 +1434,6 @@ function registerBudget(pi, isEnabled, settingsManager) {
|
|
|
1433
1434
|
pi.on("session_tree", resetForTransition);
|
|
1434
1435
|
pi.on("model_select", resetForTransition);
|
|
1435
1436
|
pi.on("session_shutdown", resetForTransition);
|
|
1436
|
-
pi.on("turn_start", (_event, ctx) => notifyCommittedReminders(ctx));
|
|
1437
1437
|
pi.on("agent_settled", (_event, ctx) => {
|
|
1438
1438
|
notifyCommittedReminders(ctx);
|
|
1439
1439
|
clearStaged();
|
|
@@ -1450,7 +1450,7 @@ function registerBudget(pi, isEnabled, settingsManager) {
|
|
|
1450
1450
|
const content = `${GUIDANCE_OPEN_TAG}
|
|
1451
1451
|
${WARNING_PROMPT}
|
|
1452
1452
|
${GUIDANCE_CLOSE_TAG}`;
|
|
1453
|
-
pendingWarning = { windowId, content };
|
|
1453
|
+
pendingWarning = { windowId, content, remaining };
|
|
1454
1454
|
const warningMessage = {
|
|
1455
1455
|
role: "custom",
|
|
1456
1456
|
customType: WARNING_TYPE,
|
|
@@ -1463,7 +1463,7 @@ ${GUIDANCE_CLOSE_TAG}`;
|
|
|
1463
1463
|
if (hasWindowMessage(ctx, GUIDANCE_TYPE) || pendingGuidance?.windowId === windowId) return void 0;
|
|
1464
1464
|
if (remaining <= reminder) {
|
|
1465
1465
|
const left = Math.max(0, remaining - warning);
|
|
1466
|
-
pendingGuidance = { windowId, content: tokenBudgetGuidance(left) };
|
|
1466
|
+
pendingGuidance = { windowId, content: tokenBudgetGuidance(left), remaining };
|
|
1467
1467
|
}
|
|
1468
1468
|
return void 0;
|
|
1469
1469
|
});
|
|
@@ -40,6 +40,7 @@ export function registerBudget(pi, isEnabled, settingsManager) {
|
|
|
40
40
|
return usage !== undefined && usage.tokens !== null && usage.contextWindow - usage.tokens <= thresholdsFor(ctx).reserve;
|
|
41
41
|
};
|
|
42
42
|
const invalidateThresholds = () => { cachedPolicy = undefined; };
|
|
43
|
+
const formatRemaining = (remaining) => `${Math.max(0, Math.ceil(remaining / 1000))}k`;
|
|
43
44
|
let pendingGuidance;
|
|
44
45
|
let pendingWarning;
|
|
45
46
|
let pendingNotices = [];
|
|
@@ -49,8 +50,8 @@ export function registerBudget(pi, isEnabled, settingsManager) {
|
|
|
49
50
|
if (notice.windowId !== windowId || !hasWindowMessage(ctx, notice.customType))
|
|
50
51
|
continue;
|
|
51
52
|
ctx.ui.notify(notice.customType === WARNING_TYPE
|
|
52
|
-
?
|
|
53
|
-
:
|
|
53
|
+
? `pi-context: Context almost full — ${formatRemaining(notice.remaining)} remaining`
|
|
54
|
+
: `pi-context: Context running low — ${formatRemaining(notice.remaining)} remaining`, "warning");
|
|
54
55
|
}
|
|
55
56
|
pendingNotices = [];
|
|
56
57
|
};
|
|
@@ -72,7 +73,7 @@ export function registerBudget(pi, isEnabled, settingsManager) {
|
|
|
72
73
|
clearStaged();
|
|
73
74
|
const windowId = currentWindowId(ctx);
|
|
74
75
|
const drafts = staged.filter((draft) => draft !== undefined && draft.windowId === windowId);
|
|
75
|
-
pendingNotices = drafts.map(({ windowId, customType }) => ({ windowId, customType }));
|
|
76
|
+
pendingNotices = drafts.map(({ windowId, customType, remaining }) => ({ windowId, customType, remaining }));
|
|
76
77
|
return drafts.map((draft) => ({
|
|
77
78
|
type: "custom_message",
|
|
78
79
|
customType: draft.customType,
|
|
@@ -88,7 +89,6 @@ export function registerBudget(pi, isEnabled, settingsManager) {
|
|
|
88
89
|
// lifecycle point that must discard an uncommitted draft before the next prompt.
|
|
89
90
|
// UI notices follow committed reminders. Aborted requests can retry their drafts
|
|
90
91
|
// without showing the same low-budget notification twice.
|
|
91
|
-
pi.on("turn_start", (_event, ctx) => notifyCommittedReminders(ctx));
|
|
92
92
|
pi.on("agent_settled", (_event, ctx) => {
|
|
93
93
|
notifyCommittedReminders(ctx);
|
|
94
94
|
clearStaged();
|
|
@@ -109,7 +109,7 @@ export function registerBudget(pi, isEnabled, settingsManager) {
|
|
|
109
109
|
// A not-yet-committed shallow reminder is superseded by the final warning.
|
|
110
110
|
pendingGuidance = undefined;
|
|
111
111
|
const content = `${GUIDANCE_OPEN_TAG}\n${WARNING_PROMPT}\n${GUIDANCE_CLOSE_TAG}`;
|
|
112
|
-
pendingWarning = { windowId, content };
|
|
112
|
+
pendingWarning = { windowId, content, remaining };
|
|
113
113
|
const warningMessage = {
|
|
114
114
|
role: "custom",
|
|
115
115
|
customType: WARNING_TYPE,
|
|
@@ -125,7 +125,7 @@ export function registerBudget(pi, isEnabled, settingsManager) {
|
|
|
125
125
|
// Persist at turn_end, before any reset drafts. A queued sendMessage could
|
|
126
126
|
// otherwise cross the marker and leak the old window's reminder forward.
|
|
127
127
|
const left = Math.max(0, remaining - warning);
|
|
128
|
-
pendingGuidance = { windowId, content: tokenBudgetGuidance(left) };
|
|
128
|
+
pendingGuidance = { windowId, content: tokenBudgetGuidance(left), remaining };
|
|
129
129
|
}
|
|
130
130
|
return undefined;
|
|
131
131
|
});
|
|
@@ -59,6 +59,7 @@ async function openFixture(options) {
|
|
|
59
59
|
const streamSignals = [];
|
|
60
60
|
const events = [];
|
|
61
61
|
const notices = [];
|
|
62
|
+
let budgetNotices = 0;
|
|
62
63
|
let session;
|
|
63
64
|
const loader = new DefaultResourceLoader({
|
|
64
65
|
cwd: dir,
|
|
@@ -120,16 +121,14 @@ async function openFixture(options) {
|
|
|
120
121
|
};
|
|
121
122
|
await session.bindExtensions({
|
|
122
123
|
uiContext: {
|
|
123
|
-
notify(message) {
|
|
124
|
+
notify(message, type) {
|
|
124
125
|
if (message.startsWith("pi-context: memory cleared · ")) {
|
|
125
126
|
const windowId = message.split(" · ")[1];
|
|
126
127
|
assert.ok(sessionManager.getBranch().some((entry) => entry.type === "custom" && entry.customType === RESET_MARKER_TYPE && entry.data?.windowId === windowId), "notification follows the reset marker commit");
|
|
127
128
|
assert.ok(sessionManager.getBranch().some((entry) => entry.type === "custom_message" && entry.customType === BOOT_TYPE && entry.details?.windowId === windowId), "notification follows the reset boot commit");
|
|
128
129
|
}
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
assert.ok(sessionManager.getBranch().some((entry) => entry.type === "custom_message" && entry.customType === customType), "budget notifications follow the reminder commit");
|
|
132
|
-
}
|
|
130
|
+
if (type === "warning" && sessionManager.getBranch().some((entry) => entry.type === "custom_message" && (entry.customType === GUIDANCE_TYPE || entry.customType === WARNING_TYPE)))
|
|
131
|
+
budgetNotices++;
|
|
133
132
|
notices.push(message);
|
|
134
133
|
},
|
|
135
134
|
},
|
|
@@ -145,6 +144,7 @@ async function openFixture(options) {
|
|
|
145
144
|
streamSignals,
|
|
146
145
|
events,
|
|
147
146
|
notices,
|
|
147
|
+
budgetNotices: () => budgetNotices,
|
|
148
148
|
close: () => {
|
|
149
149
|
session.dispose();
|
|
150
150
|
if (managesEnvironment) {
|
|
@@ -221,22 +221,22 @@ test("real AgentSession: aborted low-budget requests notify only after a retry c
|
|
|
221
221
|
});
|
|
222
222
|
try {
|
|
223
223
|
const reminders = () => fixture.sessionManager.getBranch().filter((entry) => entry.type === "custom_message" && entry.customType === GUIDANCE_TYPE);
|
|
224
|
-
const notices = () => fixture.
|
|
224
|
+
const notices = () => fixture.budgetNotices();
|
|
225
225
|
await fixture.session.prompt("Establish usage below the reminder line.");
|
|
226
226
|
await fixture.session.waitForIdle();
|
|
227
227
|
assert.equal(reminders().length, 0);
|
|
228
228
|
await fixture.session.prompt("Abort this low-budget request.");
|
|
229
229
|
await fixture.session.waitForIdle();
|
|
230
230
|
assert.equal(reminders().length, 0, "an aborted turn does not commit its reminder");
|
|
231
|
-
assert.equal(notices()
|
|
231
|
+
assert.equal(notices(), 0, "an uncommitted reminder never notifies");
|
|
232
232
|
await fixture.session.prompt("Retry successfully.");
|
|
233
233
|
await fixture.session.waitForIdle();
|
|
234
234
|
assert.equal(reminders().length, 1);
|
|
235
|
-
assert.equal(notices()
|
|
235
|
+
assert.equal(notices(), 1, "the committed retry notifies once");
|
|
236
236
|
await fixture.session.prompt("Continue in the same window.");
|
|
237
237
|
await fixture.session.waitForIdle();
|
|
238
238
|
assert.equal(reminders().length, 1);
|
|
239
|
-
assert.equal(notices()
|
|
239
|
+
assert.equal(notices(), 1, "later turns cannot repeat the notice");
|
|
240
240
|
}
|
|
241
241
|
finally {
|
|
242
242
|
fixture.close();
|
package/package.json
CHANGED
package/src/context/budget.ts
CHANGED
|
@@ -38,16 +38,17 @@ export function registerBudget(pi: ExtensionAPI, isEnabled: () => boolean, setti
|
|
|
38
38
|
return usage !== undefined && usage.tokens !== null && usage.contextWindow - usage.tokens <= thresholdsFor(ctx).reserve;
|
|
39
39
|
};
|
|
40
40
|
const invalidateThresholds = () => { cachedPolicy = undefined; };
|
|
41
|
-
|
|
42
|
-
let
|
|
43
|
-
let
|
|
41
|
+
const formatRemaining = (remaining: number): string => `${Math.max(0, Math.ceil(remaining / 1000))}k`;
|
|
42
|
+
let pendingGuidance: { windowId: string; content: string; remaining: number } | undefined;
|
|
43
|
+
let pendingWarning: { windowId: string; content: string; remaining: number } | undefined;
|
|
44
|
+
let pendingNotices: Array<{ windowId: string; customType: string; remaining: number }> = [];
|
|
44
45
|
const notifyCommittedReminders = (ctx: ExtensionContext) => {
|
|
45
46
|
const windowId = currentWindowId(ctx);
|
|
46
47
|
for (const notice of pendingNotices) {
|
|
47
48
|
if (notice.windowId !== windowId || !hasWindowMessage(ctx, notice.customType)) continue;
|
|
48
49
|
ctx.ui.notify(notice.customType === WARNING_TYPE
|
|
49
|
-
?
|
|
50
|
-
:
|
|
50
|
+
? `pi-context: Context almost full — ${formatRemaining(notice.remaining)} remaining`
|
|
51
|
+
: `pi-context: Context running low — ${formatRemaining(notice.remaining)} remaining`, "warning");
|
|
51
52
|
}
|
|
52
53
|
pendingNotices = [];
|
|
53
54
|
};
|
|
@@ -71,7 +72,7 @@ export function registerBudget(pi: ExtensionAPI, isEnabled: () => boolean, setti
|
|
|
71
72
|
clearStaged();
|
|
72
73
|
const windowId = currentWindowId(ctx);
|
|
73
74
|
const drafts = staged.filter((draft): draft is NonNullable<typeof draft> => draft !== undefined && draft.windowId === windowId);
|
|
74
|
-
pendingNotices = drafts.map(({ windowId, customType }) => ({ windowId, customType }));
|
|
75
|
+
pendingNotices = drafts.map(({ windowId, customType, remaining }) => ({ windowId, customType, remaining }));
|
|
75
76
|
return drafts.map((draft) => ({
|
|
76
77
|
type: "custom_message" as const,
|
|
77
78
|
customType: draft.customType,
|
|
@@ -88,7 +89,6 @@ export function registerBudget(pi: ExtensionAPI, isEnabled: () => boolean, setti
|
|
|
88
89
|
// lifecycle point that must discard an uncommitted draft before the next prompt.
|
|
89
90
|
// UI notices follow committed reminders. Aborted requests can retry their drafts
|
|
90
91
|
// without showing the same low-budget notification twice.
|
|
91
|
-
pi.on("turn_start", (_event, ctx) => notifyCommittedReminders(ctx));
|
|
92
92
|
pi.on("agent_settled", (_event, ctx) => {
|
|
93
93
|
notifyCommittedReminders(ctx);
|
|
94
94
|
clearStaged();
|
|
@@ -106,7 +106,7 @@ export function registerBudget(pi: ExtensionAPI, isEnabled: () => boolean, setti
|
|
|
106
106
|
// A not-yet-committed shallow reminder is superseded by the final warning.
|
|
107
107
|
pendingGuidance = undefined;
|
|
108
108
|
const content = `${GUIDANCE_OPEN_TAG}\n${WARNING_PROMPT}\n${GUIDANCE_CLOSE_TAG}`;
|
|
109
|
-
pendingWarning = { windowId, content };
|
|
109
|
+
pendingWarning = { windowId, content, remaining };
|
|
110
110
|
const warningMessage = {
|
|
111
111
|
role: "custom" as const,
|
|
112
112
|
customType: WARNING_TYPE,
|
|
@@ -121,7 +121,7 @@ export function registerBudget(pi: ExtensionAPI, isEnabled: () => boolean, setti
|
|
|
121
121
|
// Persist at turn_end, before any reset drafts. A queued sendMessage could
|
|
122
122
|
// otherwise cross the marker and leak the old window's reminder forward.
|
|
123
123
|
const left = Math.max(0, remaining - warning);
|
|
124
|
-
pendingGuidance = { windowId, content: tokenBudgetGuidance(left) };
|
|
124
|
+
pendingGuidance = { windowId, content: tokenBudgetGuidance(left), remaining };
|
|
125
125
|
}
|
|
126
126
|
return undefined;
|
|
127
127
|
});
|