pi-message-sidebar 1.2.0 → 1.3.0
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/message-sidebar.ts +2 -2
- package/package.json +1 -1
- package/src/goal.ts +38 -14
package/message-sidebar.ts
CHANGED
|
@@ -125,8 +125,8 @@ export default function messageSidebar(pi: ExtensionAPI): void {
|
|
|
125
125
|
|
|
126
126
|
ctx.ui.onTerminalInput((data) => {
|
|
127
127
|
// Slash commands such as /goal mutate session entries without firing
|
|
128
|
-
// turn events
|
|
129
|
-
scheduleRefresh();
|
|
128
|
+
// turn events. Refresh on submit, not every keystroke.
|
|
129
|
+
if (matchesKey(data, "return") || matchesKey(data, "enter")) scheduleRefresh();
|
|
130
130
|
if (tui && !isSidebarVisible(tui.terminal.columns) && sidebar?.isFocused()) {
|
|
131
131
|
sidebar.setFocused(false);
|
|
132
132
|
tui.requestRender();
|
package/package.json
CHANGED
package/src/goal.ts
CHANGED
|
@@ -15,22 +15,26 @@ type BranchEntry = { type?: string; customType?: string; data?: unknown };
|
|
|
15
15
|
|
|
16
16
|
const GOAL_ENTRY_TYPE = "pi-codex-goal";
|
|
17
17
|
|
|
18
|
-
function isThreadGoal(value: unknown): value is
|
|
19
|
-
const goal = value as
|
|
18
|
+
function isThreadGoal(value: unknown): value is RawGoal {
|
|
19
|
+
const goal = value as RawGoal | null;
|
|
20
|
+
if (!goal || typeof goal !== "object") return false;
|
|
21
|
+
const usage = goal.usage as { tokensUsed?: unknown; activeSeconds?: unknown } | undefined;
|
|
20
22
|
return (
|
|
21
|
-
!!goal &&
|
|
22
|
-
typeof goal === "object" &&
|
|
23
23
|
typeof goal.goalId === "string" &&
|
|
24
24
|
typeof goal.objective === "string" &&
|
|
25
25
|
(goal.status === "active" || goal.status === "paused" || goal.status === "budgetLimited" || goal.status === "complete") &&
|
|
26
26
|
(goal.tokenBudget === null || typeof goal.tokenBudget === "number") &&
|
|
27
|
-
typeof goal.tokensUsed === "number" &&
|
|
28
|
-
typeof goal.activeSeconds === "number" &&
|
|
29
27
|
typeof goal.createdAt === "number" &&
|
|
30
|
-
typeof goal.updatedAt === "number"
|
|
28
|
+
typeof goal.updatedAt === "number" &&
|
|
29
|
+
typeof usage?.tokensUsed === "number" &&
|
|
30
|
+
typeof usage.activeSeconds === "number"
|
|
31
31
|
);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
type RawGoal = Omit<ThreadGoal, "tokensUsed" | "activeSeconds"> & {
|
|
35
|
+
usage: { tokensUsed: number; activeSeconds: number };
|
|
36
|
+
};
|
|
37
|
+
|
|
34
38
|
/**
|
|
35
39
|
* Reconstructs the current thread goal from pi-codex-goal session entries.
|
|
36
40
|
* Mirrors reconstructGoal() from pi-codex-goal dist/state.js (schema version 1):
|
|
@@ -46,31 +50,51 @@ export function readThreadGoal(entries: Iterable<BranchEntry>): ThreadGoal | nul
|
|
|
46
50
|
if (data.kind === "clear") {
|
|
47
51
|
goal = null;
|
|
48
52
|
} else if (data.kind === "set" && isThreadGoal(data.goal)) {
|
|
49
|
-
|
|
53
|
+
const raw = data.goal as RawGoal;
|
|
54
|
+
goal = {
|
|
55
|
+
goalId: raw.goalId,
|
|
56
|
+
objective: raw.objective,
|
|
57
|
+
status: raw.status,
|
|
58
|
+
tokenBudget: raw.tokenBudget,
|
|
59
|
+
tokensUsed: raw.usage.tokensUsed,
|
|
60
|
+
activeSeconds: raw.usage.activeSeconds,
|
|
61
|
+
createdAt: raw.createdAt,
|
|
62
|
+
updatedAt: raw.updatedAt,
|
|
63
|
+
};
|
|
50
64
|
} else if (data.kind === "usage" && goal) {
|
|
51
65
|
const usage = data.usage as { tokensUsed?: number; activeSeconds?: number } | undefined;
|
|
52
66
|
const updatedAt = data.updatedAt;
|
|
67
|
+
const status = data.status;
|
|
53
68
|
if (data.goalId !== goal.goalId) continue;
|
|
69
|
+
if (status !== "active" && status !== "budgetLimited") continue;
|
|
54
70
|
if (goal.status !== "active" && goal.status !== "budgetLimited") continue;
|
|
55
|
-
if (goal.status === "budgetLimited" &&
|
|
71
|
+
if (goal.status === "budgetLimited" && status === "active") continue;
|
|
56
72
|
if (typeof updatedAt !== "number" || typeof usage?.tokensUsed !== "number" || typeof usage.activeSeconds !== "number") continue;
|
|
57
73
|
if (updatedAt < goal.updatedAt || usage.tokensUsed < goal.tokensUsed || usage.activeSeconds < goal.activeSeconds) continue;
|
|
58
|
-
|
|
74
|
+
const current: ThreadGoal = goal;
|
|
75
|
+
goal = {
|
|
76
|
+
...current,
|
|
77
|
+
status,
|
|
78
|
+
tokensUsed: usage.tokensUsed,
|
|
79
|
+
activeSeconds: usage.activeSeconds,
|
|
80
|
+
updatedAt,
|
|
81
|
+
};
|
|
59
82
|
}
|
|
60
83
|
}
|
|
61
84
|
return goal;
|
|
62
85
|
}
|
|
63
86
|
|
|
64
|
-
type GoalCache = { key: string; goal: ThreadGoal | null };
|
|
65
|
-
|
|
87
|
+
type GoalCache = { branch: object; key: string; goal: ThreadGoal | null };
|
|
88
|
+
const cache = new WeakMap<ExtensionContext, GoalCache>();
|
|
66
89
|
|
|
67
90
|
/** Cached readThreadGoal over the current session branch. */
|
|
68
91
|
export function readSessionGoal(ctx: ExtensionContext): ThreadGoal | null {
|
|
69
92
|
const branch = ctx.sessionManager.getBranch();
|
|
70
93
|
const last = branch.at(-1);
|
|
71
94
|
const key = `${branch.length}:${last?.id ?? ""}:${last?.timestamp ?? ""}`;
|
|
72
|
-
|
|
95
|
+
const previous = cache.get(ctx);
|
|
96
|
+
if (previous?.branch === branch && previous.key === key) return previous.goal;
|
|
73
97
|
const goal = readThreadGoal(branch);
|
|
74
|
-
cache
|
|
98
|
+
cache.set(ctx, { branch, key, goal });
|
|
75
99
|
return goal;
|
|
76
100
|
}
|