@quantmind-br/pi-goal-x 0.0.0-stage → 0.35.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/LICENSE +21 -0
- package/README.md +131 -2
- package/assets/badge-dark.svg +18 -0
- package/assets/badge-light.svg +18 -0
- package/docs/advanced-usage.md +106 -0
- package/docs/agent-flow-design.md +306 -0
- package/docs/agentic-runtime-prd.md +773 -0
- package/docs/architecture.md +368 -0
- package/docs/controls.md +140 -0
- package/docs/pi-autoresearch-survey.md +50 -0
- package/docs/pi-goal-x-unified-dashboard-plan.md +1667 -0
- package/docs/prompt-caching.md +31 -0
- package/docs/unified-dashboard.md +286 -0
- package/extensions/auditor-selector.ts +74 -0
- package/extensions/goal-accounting.ts +107 -0
- package/extensions/goal-activity.ts +202 -0
- package/extensions/goal-auditor.ts +480 -0
- package/extensions/goal-commands.ts +866 -0
- package/extensions/goal-compaction.ts +197 -0
- package/extensions/goal-completion.ts +173 -0
- package/extensions/goal-context.ts +12 -0
- package/extensions/goal-contract.ts +58 -0
- package/extensions/goal-controls.ts +202 -0
- package/extensions/goal-core-tools.ts +549 -0
- package/extensions/goal-core.ts +78 -0
- package/extensions/goal-detail.ts +65 -0
- package/extensions/goal-dialog-controls.ts +31 -0
- package/extensions/goal-draft.ts +244 -0
- package/extensions/goal-drafting.ts +535 -0
- package/extensions/goal-events.ts +692 -0
- package/extensions/goal-format.ts +376 -0
- package/extensions/goal-interruption.ts +53 -0
- package/extensions/goal-ledger-index.ts +68 -0
- package/extensions/goal-ledger.ts +1007 -0
- package/extensions/goal-live-retention.ts +219 -0
- package/extensions/goal-oracle.ts +346 -0
- package/extensions/goal-policy.ts +370 -0
- package/extensions/goal-pool.ts +103 -0
- package/extensions/goal-prompt-cache.ts +80 -0
- package/extensions/goal-questionnaire.ts +1073 -0
- package/extensions/goal-record.ts +388 -0
- package/extensions/goal-recovery.ts +256 -0
- package/extensions/goal-runtime.ts +277 -0
- package/extensions/goal-scheduler-state.ts +100 -0
- package/extensions/goal-scheduler.ts +436 -0
- package/extensions/goal-service.ts +843 -0
- package/extensions/goal-session-health.ts +130 -0
- package/extensions/goal-session-safety.ts +65 -0
- package/extensions/goal-settings.ts +1370 -0
- package/extensions/goal-state.ts +1069 -0
- package/extensions/goal-status.ts +305 -0
- package/extensions/goal-task-confirmation.ts +174 -0
- package/extensions/goal-task-count.ts +59 -0
- package/extensions/goal-task-derive.ts +46 -0
- package/extensions/goal-task-index.ts +57 -0
- package/extensions/goal-task-tools.ts +595 -0
- package/extensions/goal-tool-inputs.ts +102 -0
- package/extensions/goal-tool-names.ts +73 -0
- package/extensions/goal-tools.ts +16 -0
- package/extensions/goal-widget.ts +455 -0
- package/extensions/goal.ts +42 -0
- package/extensions/network-error-backoff.ts +55 -0
- package/extensions/prompts/goal-prompts.ts +321 -0
- package/extensions/storage/atomic-rename.ts +52 -0
- package/extensions/storage/goal-files.ts +695 -0
- package/extensions/storage/goal-lock.ts +131 -0
- package/extensions/storage/goal-root.ts +95 -0
- package/extensions/widgets/auditor-dashboard-model.ts +174 -0
- package/extensions/widgets/dialog-scaffold.ts +26 -0
- package/extensions/widgets/goal-controls-dialog.ts +329 -0
- package/extensions/widgets/goal-dashboard-model.ts +530 -0
- package/extensions/widgets/goal-dashboard-renderer.ts +674 -0
- package/extensions/widgets/goal-escape-dialog.ts +148 -0
- package/extensions/widgets/goal-notifications.ts +9 -0
- package/extensions/widgets/goal-widget.ts +723 -0
- package/extensions/widgets/task-list-overlay.ts +409 -0
- package/extensions/widgets/text-cache.ts +30 -0
- package/package.json +117 -4
- package/scripts/recover-session-checkpoints.mjs +289 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Prompt caching
|
|
2
|
+
|
|
3
|
+
pi-goal-x preserves the provider's reusable prompt prefix during normal user turns, autonomous checkpoints, and tool loops. No extra extension setting is required. Pi's provider configuration still determines whether caching is enabled, its retention period, and model support.
|
|
4
|
+
|
|
5
|
+
The request layout is:
|
|
6
|
+
|
|
7
|
+
1. Host system prompt and tool schemas.
|
|
8
|
+
2. Conversation history, with each goal checkpoint normalized to a tiny, stable trigger in its original position.
|
|
9
|
+
3. Request-only goal policy plus compact counter/context snapshots, replayed at verified history anchors when safe. Current policy includes the objective, tasks, lifecycle rules, limits, scheduling state and applicable recovery/auditor reminders.
|
|
10
|
+
|
|
11
|
+
Changing goal state never rewrites the system prompt or historical messages. The full goal-state message is not persisted into the session. Old checkpoints remain until Pi compacts history; retaining small markers avoids shifting the prefix on every continuation. Display-only auditor messages are consistently excluded, and tool calls stay adjacent to their results.
|
|
12
|
+
|
|
13
|
+
Goal context filtering and live-state tails use Pi's `context_with_system` event, preserving system and tool deltas in their original positions. Parent and delegated sessions use this hook from their first request.
|
|
14
|
+
|
|
15
|
+
For Anthropic-style explicit caching, the extension moves Pi's existing cache_control breakpoint from the transient state message onto the preceding cacheable history block. The Bedrock equivalent moves the cachePoint. Both preserve the configured TTL and do not add breakpoints. Payloads without an existing explicit marker—including caching disabled and implicit-cache providers—receive no marker changes. The extension does not set cache keys, enable paid extended retention, or replace provider defaults.
|
|
16
|
+
|
|
17
|
+
Goal and drafting tools share a fixed, idempotent profile: goal progress, focus, lifecycle changes, and draft start/confirm/cancel do not change tool declarations or their prompt rules. Drafting tools reject calls without an active user-started draft. While a draft is active, an execution guard blocks the five ordinary goal tools until confirmation or cancellation. Only changes to disableTasks alter this extension's tool selection and legitimately rebuild the cache. Host tools remain untouched. Drafting messages are appended to conversation history. Isolated auditor and Oracle sessions already use fixed instructions/tool profiles and place goal-specific evidence in their user messages; their tool loops retain history. They have separate sessions and use Pi's cache defaults. Opting these sessions into project resources also opts into any behavior from those resources.
|
|
18
|
+
|
|
19
|
+
Cache reuse can still be interrupted by Pi compaction, branch/session changes, model/provider switches, other extensions changing earlier content, changed tool schemas, provider expiry, minimum-length or breakpoint-lookback limits, or routing. The extension cannot guarantee a hit rate or a particular cost reduction. The supported host range is Pi 1.4+ within 1.x, with Node 22.19 or newer. Development dependencies pin Pi 1.4.0.
|
|
20
|
+
|
|
21
|
+
Pi 1.4 prepares custom-triggered turns through the same public startup protocol as user prompts. The extension leaves structured prompt and tool deltas intact, including additional sections supplied by other extensions. Direct creation, drafting, resume and reload use the host's native prompt preparation from the first request. Explicit tool-profile changes can still rebuild the prefix.
|
|
22
|
+
|
|
23
|
+
Validation includes consecutive request-prefix regressions; actual Anthropic, Anthropic-compatible Chat Completions, OpenAI Chat Completions and Responses serialization with short/long/disabled retention; Bedrock payload-shape checks; lifecycle/tool-pair safety tests; and full SDK parent/auditor/Oracle payload captures. These tests stop before HTTP dispatch. They verify request construction, not live provider hit rates.
|
|
24
|
+
|
|
25
|
+
Provider references: [Anthropic prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) describes exact-prefix matching and explicit breakpoints; [DeepSeek context caching](https://api-docs.deepseek.com/guides/kv_cache/) describes its automatic prefix cache and usage counters.
|
|
26
|
+
|
|
27
|
+
Active requests split policy from compact counter/context snapshots. Already-sent tails are replayed at verified history anchors so implicit-cache requests extend their previous prefix. Policy changes (including budget/run-limit removal), rewritten history and unsafe tool-result boundaries reset retention. At most 32 tails and 4 KiB of counter text are retained per session, across at most 16 session/model identities; no full-state tails are persisted. Unchanged requests add no tail. Older snapshots are historical, and current policy takes precedence over cache reuse.
|
|
28
|
+
|
|
29
|
+
Explicit-marker relocation skips all retained live text and tolerates Pi's trailing effort-only messages. Another extension's content-bearing suffix is left alone. Changing models/providers starts a separate retention identity. Unknown session identities do not retain history between requests.
|
|
30
|
+
|
|
31
|
+
Validation now includes advancing SDK request chains, combined effort-marker/retention regressions, 1,000-turn growth bounds and a 64 KiB–4 MiB history-cost gate. These are request-construction and CPU measurements, not live provider hit-rate claims.
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# Unified Dashboard
|
|
2
|
+
|
|
3
|
+
`pi-goal-x` renders one dashboard component in two presentation modes. The
|
|
4
|
+
above-editor widget, `/goal-status`, and the completion flow all derive from
|
|
5
|
+
the same presentation model (`extensions/widgets/goal-dashboard-model.ts`),
|
|
6
|
+
so they can never drift apart on data or terminology.
|
|
7
|
+
|
|
8
|
+
- **Compact mode** — a persistent summary above the editor while a goal is
|
|
9
|
+
focused.
|
|
10
|
+
- **Expanded mode** — the full dashboard: task tree, current-task details,
|
|
11
|
+
verification, and recent activity. This replaces the former task-list
|
|
12
|
+
overlay; `Ctrl+Shift+T` now toggles between compact and expanded instead of
|
|
13
|
+
opening a separate overlay.
|
|
14
|
+
|
|
15
|
+
Everything shown is derived from persisted goal state and the durable ledger:
|
|
16
|
+
progress, the current task, verification status, the audit result, and recent
|
|
17
|
+
activity are never fabricated for display.
|
|
18
|
+
|
|
19
|
+
## Compact mode
|
|
20
|
+
|
|
21
|
+
Always visible above the editor while a goal is focused:
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
╭─ pi-goal-x ─ Add CSV export to reports ────────────────────────────────────────────────╮
|
|
25
|
+
│ goal: running [12m47s 18.2K] (+2 open) │
|
|
26
|
+
├─ Tasks · ✓3 done · 2 open ─────────────────────────────────── [████░░] · Sub 2/3 [██░] ┤
|
|
27
|
+
│ ✓ t1 Review reports page and data source │
|
|
28
|
+
│ ✓ t2 Implement filtered CSV export │
|
|
29
|
+
│ ▸ t3 Add the download button ☑ ▸ 2/3 │
|
|
30
|
+
│ · t4 Add documentation │
|
|
31
|
+
│ … +1 more task │
|
|
32
|
+
│ Current t3 · Add the download button │
|
|
33
|
+
│ Verify Run npm test with zero failures. │
|
|
34
|
+
│ File .pi/goals/active_goal_g1.md │
|
|
35
|
+
╰─ Ctrl+Shift+T: expand tasks─────────────────────────── Ctrl+Shift+A: toggle auditor ● ─╯
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Compact rows, in order:
|
|
39
|
+
|
|
40
|
+
1. Header (rounded corners): title. (Usage lives in the status line below,
|
|
41
|
+
not in the header.)
|
|
42
|
+
2. Status: `goal: <status> [<elapsed> <tokens>] (+N open)` — the same
|
|
43
|
+
terminology as the pi footer status line.
|
|
44
|
+
3. Token budget (when configured): `⛽ Budget 18.2K / 50K · 36%`.
|
|
45
|
+
4. Tasks header row: `Tasks · ✓N done · M open` with the compact task
|
|
46
|
+
progress bar, then the current task's subtask bar beside it
|
|
47
|
+
(` · Sub done/total ` + bar — the word `Sub` is dropped in narrow mode so
|
|
48
|
+
the full counts still fit at 50 columns, and the segment is omitted
|
|
49
|
+
entirely in minimal mode, where the counts and task bar alone use all
|
|
50
|
+
available space). `N` counts top-level tasks complete **or skipped** — a
|
|
51
|
+
skipped top-level task counts as done (§9.1) — and `M` counts pending
|
|
52
|
+
tasks. The subtask segment appears only when the current task has direct
|
|
53
|
+
children. The whole header row is neutral gray (label, counts, bar fill,
|
|
54
|
+
and empty cells) so nothing in the progress info is colourful.
|
|
55
|
+
6. Task list section: the top-level tasks shown by default in **pastel
|
|
56
|
+
amber** (`mdHeading`) with colour-coded markers and ids (✓ complete muted
|
|
57
|
+
green, ▸ current teal, ~ skipped gray, · pending amber), an aligned id
|
|
58
|
+
column, truncated amber titles, and a `… +N more` overflow line when the
|
|
59
|
+
list is longer than the row budget (5 rows at wide, 4 medium, 3 narrow, 2
|
|
60
|
+
minimal). Tasks that have direct subtasks show a muted `▸ done/total`
|
|
61
|
+
marker at the end of their row (leaf tasks show none). The current task
|
|
62
|
+
is fully highlighted in the theme's accent color (marker, id, title). The
|
|
63
|
+
whole box chrome — top/bottom frame, left/right edges, corners and
|
|
64
|
+
interior rules — is drawn in one tone: the theme's neutral gray
|
|
65
|
+
(`muted`). No blue tinge.
|
|
66
|
+
6. Current task: `Current t3 · Add the download button` (id and title in the
|
|
67
|
+
accent color).
|
|
68
|
+
7. Goal-level verification contract (first line, truncated).
|
|
69
|
+
8. Blocked or paused detail (reason, suggested action).
|
|
70
|
+
9. Active (or archived) goal file path.
|
|
71
|
+
10. Footer with the expansion shortcut. The whole footer line — leading dash,
|
|
72
|
+
shortcut hint and trailing fill — is drawn in one frame tone (the
|
|
73
|
+
neutral-gray `muted`), so the color spans the full width with no two-tone
|
|
74
|
+
split; the header line is likewise one tone with only the `pi-goal-x`
|
|
75
|
+
brand in accent. The auditor status lives in the bottom-right of this
|
|
76
|
+
border: a single `●` — green when the focused goal's independent auditor
|
|
77
|
+
is on, gray when off — drawn right before the `╯` corner. In wide/medium
|
|
78
|
+
layouts the footer also right-aligns a `Ctrl+Shift+A: toggle auditor`
|
|
79
|
+
note beside the dot, making explicit that the shortcut turns the auditor
|
|
80
|
+
on and off; narrow and minimal keep just the dot. `Ctrl+Shift+A` toggles
|
|
81
|
+
the auditor per-goal (persisted, `auditor_toggled` ledger event,
|
|
82
|
+
dashboard refresh, notification; inert with no focused goal, a goal modal
|
|
83
|
+
open, or a complete goal). The expanded dashboard does not render the
|
|
84
|
+
auditor indicator.
|
|
85
|
+
|
|
86
|
+
When every top-level task is done, the current line reads
|
|
87
|
+
`Current All tasks complete`. With `disableTasks` enabled the task rows are
|
|
88
|
+
omitted entirely.
|
|
89
|
+
|
|
90
|
+
## Expanded mode
|
|
91
|
+
|
|
92
|
+
`Ctrl+Shift+T` expands the same component:
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
╭─ pi-goal-x ─ Add CSV export to reports ─────────────── 12m47s · 18.2K tok ─╮
|
|
96
|
+
│ Status: ● In progress · Focused: yes · Other goals: 2 │
|
|
97
|
+
│ File: .pi/goals/active_goal_... │
|
|
98
|
+
├─ Progress ──────────────────────────────────────────────────────────────────┤
|
|
99
|
+
│ [██████░░░░] 3/5 tasks · 60% │
|
|
100
|
+
├─ Tasks ─────────────────────────────────────────────────────────────────────┤
|
|
101
|
+
│ ✓ t1 Review reports page and data source │
|
|
102
|
+
│ ✓ t2 Implement filtered CSV export │
|
|
103
|
+
│ ▸ t3 Add the download button ☑ │
|
|
104
|
+
│ ✓ t3.1 Add loading state │
|
|
105
|
+
│ ✓ t3.2 Generate timestamped filename │
|
|
106
|
+
│ · t3.3 Add error handling │
|
|
107
|
+
│ · t4 Add documentation │
|
|
108
|
+
│ ~ t5 Add and run tests │
|
|
109
|
+
├─ Current task ──────────────────────────────────────────────────────────────┤
|
|
110
|
+
│ t3 · Add the download button │
|
|
111
|
+
│ Subtasks [███████░░░] 2/3 · 67% │
|
|
112
|
+
│ Contract: The button downloads a CSV using the active filters. │
|
|
113
|
+
├─ Verification ──────────────────────────────────────────────────────────────┤
|
|
114
|
+
│ Run npm test with zero failures. │
|
|
115
|
+
├─ Recent activity ───────────────────────────────────────────────────────────┤
|
|
116
|
+
│ ✓ Completed “Implement filtered CSV export”. — Done │
|
|
117
|
+
│ ▸ Started “Add error handling”. │
|
|
118
|
+
╰─ Esc/Ctrl+Shift+T: collapse ────────────────────────────────────────────────╯
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Task markers (§9.2): `✓` complete, `~` skipped, `▸` current, `·` pending.
|
|
122
|
+
A `☑` suffix marks a task that carries its own verification contract; the full
|
|
123
|
+
contract is shown in the current-task block.
|
|
124
|
+
|
|
125
|
+
When no persisted current task exists, the dashboard falls back to the first
|
|
126
|
+
pending task for display and marks it as inferred — the fallback is never
|
|
127
|
+
persisted.
|
|
128
|
+
|
|
129
|
+
## Scrolling the task list
|
|
130
|
+
|
|
131
|
+
Both views show the task list as a **window** over the plan-ordered list, and
|
|
132
|
+
by default the window is **anchored to the most recently completed task** —
|
|
133
|
+
the viewport is "scrolled down" so recent completions (by `completedAt`) are
|
|
134
|
+
visible instead of always showing the earliest tasks. With nothing completed
|
|
135
|
+
the window starts at the top; when a new task completes the window re-anchors
|
|
136
|
+
to it. `↑ N more tasks` / `… +N more tasks` indicator rows mark rows hidden
|
|
137
|
+
above and below the window.
|
|
138
|
+
|
|
139
|
+
pi has no separate focus — the prompt editor owns the plain arrow keys — so
|
|
140
|
+
the two views scroll differently:
|
|
141
|
+
|
|
142
|
+
| View | Key | Action |
|
|
143
|
+
| --- | --- | --- |
|
|
144
|
+
| Expanded (modal) | `↑` / `↓` | Scroll one task row |
|
|
145
|
+
| Expanded (modal) | `PgUp` / `PgDn` | Scroll one page of task rows |
|
|
146
|
+
| Expanded (modal) | `Home` / `End` | Jump to the top / bottom of the list |
|
|
147
|
+
| Compact (inline) | `Ctrl+Shift+↑` / `Ctrl+Shift+↓` | Scroll one task row |
|
|
148
|
+
| Compact (inline) | `Ctrl+Shift+PgUp` / `Ctrl+Shift+PgDn` | Scroll one page |
|
|
149
|
+
| Compact (inline) | `Ctrl+Shift+Home` / `Ctrl+Shift+End` | Jump to the top / bottom |
|
|
150
|
+
|
|
151
|
+
While the expanded dashboard is open it is modal and owns the plain arrow
|
|
152
|
+
keys (like the session tree or model selector); `Esc` collapses it. The
|
|
153
|
+
compact widget never touches the editor's arrows — it scrolls with the
|
|
154
|
+
`Ctrl+Shift` chords, which pi leaves unbound, so typing and navigation are
|
|
155
|
+
never hijacked. The chords are consumed only when the compact list actually
|
|
156
|
+
overflows its viewport; on a short list they pass through unused. When the
|
|
157
|
+
list overflows, the compact footer advertises the chords
|
|
158
|
+
(`Ctrl+Shift+↑↓: scroll`). `/goal-status` renders the same anchored window as
|
|
159
|
+
a static snapshot (it is not interactive). The window and its indicators are
|
|
160
|
+
width-safe at every layout width.
|
|
161
|
+
|
|
162
|
+
## Status states
|
|
163
|
+
|
|
164
|
+
| Symbol | Status | Shown for |
|
|
165
|
+
| --- | --- | --- |
|
|
166
|
+
| `●` | In progress | active goal with auto-continue on |
|
|
167
|
+
| `○` | Idle | active goal with auto-continue off |
|
|
168
|
+
| `◐` | Paused (user/agent) | paused goal, with reason |
|
|
169
|
+
| `⊘` | Blocked | blocked goal, with blocker and suggested action |
|
|
170
|
+
| `⛽` | Budget limited | goal at its token budget, with usage detail |
|
|
171
|
+
| `✓` | Complete | completed goal, before/after archival |
|
|
172
|
+
|
|
173
|
+
Status labels are always explicit words, so the dashboard stays readable
|
|
174
|
+
without the symbols.
|
|
175
|
+
|
|
176
|
+
## Unfocused state
|
|
177
|
+
|
|
178
|
+
When open goals exist but none is focused:
|
|
179
|
+
|
|
180
|
+
```text
|
|
181
|
+
╭─ pi-goal-x ─ Goal focus required ───────────────────────────────────────────╮
|
|
182
|
+
│ 3 open goals are available. │
|
|
183
|
+
│ Run /goal-focus to choose the goal for this session. │
|
|
184
|
+
╰─────────────────────────────────────────────────────────────────────────────╯
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Audit view
|
|
188
|
+
|
|
189
|
+
During an independent completion audit the widget switches to a structured
|
|
190
|
+
audit dashboard with the same visual system:
|
|
191
|
+
|
|
192
|
+
```text
|
|
193
|
+
╭─ Independent completion audit ─ anthropic/claude-sonnet:high ─── 2m18s ─╮
|
|
194
|
+
│ ✓ Objective and success criteria │
|
|
195
|
+
│ ✓ Verification contracts │
|
|
196
|
+
│ ✓ Tasks and recorded evidence │
|
|
197
|
+
│ ◌ Workspace inspection │
|
|
198
|
+
│ · Final decision │
|
|
199
|
+
│ [███████░░░] 72% │
|
|
200
|
+
╰─ Esc: stop audit ─────────────────────────────────────────────────────────╯
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Raw auditor tools and output are hidden by default; they appear only in
|
|
204
|
+
expanded/debug audit mode or when the audit failed and diagnostics are needed.
|
|
205
|
+
|
|
206
|
+
After the audit, a result card shows the outcome:
|
|
207
|
+
|
|
208
|
+
```text
|
|
209
|
+
╭─ Audit result ─ APPROVED ─────────────────────────────────────────────────╮
|
|
210
|
+
│ ✓ Objective satisfied. │
|
|
211
|
+
│ ✓ Verification requirements satisfied. │
|
|
212
|
+
│ ✓ Required tasks and evidence accepted. │
|
|
213
|
+
╰────────────────────────────────────────────────────────────────────────────╯
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
A rejected audit keeps the goal open, shows `CHANGES REQUIRED` with the
|
|
217
|
+
auditor's findings, and returns to the normal dashboard so work can continue.
|
|
218
|
+
|
|
219
|
+
## Keybindings
|
|
220
|
+
|
|
221
|
+
| Key | Action |
|
|
222
|
+
| --- | --- |
|
|
223
|
+
| `Ctrl+Shift+T` | Toggle the dashboard between compact and expanded |
|
|
224
|
+
| `Ctrl+Shift+A` | Toggle the focused goal's independent auditor on/off (persisted per-goal; inert with no goal, a goal modal open, or a complete goal) |
|
|
225
|
+
| `↑` / `↓`, `PgUp` / `PgDn`, `Home` / `End` | Scroll the expanded task tree (see [Scrolling the task list](#scrolling-the-task-list)) |
|
|
226
|
+
| `Ctrl+Shift+↑` / `Ctrl+Shift+↓`, `Ctrl+Shift+PgUp` / `Ctrl+Shift+PgDn`, `Ctrl+Shift+Home` / `Ctrl+Shift+End` | Scroll the compact task list |
|
|
227
|
+
| `Esc` | Collapse the expanded dashboard; otherwise pause the goal |
|
|
228
|
+
| `Esc` (during audit) | Stop the audit and choose to continue working or complete without audit |
|
|
229
|
+
|
|
230
|
+
Configure the three task shortcuts with `keybindings.dashboard.toggleExpand`,
|
|
231
|
+
`keybindings.dashboard.scrollUp`, and `keybindings.dashboard.scrollDown` in
|
|
232
|
+
`.pi/pi-goal-x-settings.json`. The defaults are `ctrl+shift+t`,
|
|
233
|
+
`ctrl+shift+up`, and `ctrl+shift+down`.
|
|
234
|
+
|
|
235
|
+
## Width behavior
|
|
236
|
+
|
|
237
|
+
Layout modes (§5.5):
|
|
238
|
+
|
|
239
|
+
- **Wide (≥100 columns):** full border, multiple fields per line, full task
|
|
240
|
+
titles, wider progress bars, current-task details.
|
|
241
|
+
- **Medium (70–99):** mostly one field per line, shorter bars, truncated
|
|
242
|
+
paths and contracts.
|
|
243
|
+
- **Narrow (50–69):** compact border, short labels, reduced metadata, one
|
|
244
|
+
task line at a time.
|
|
245
|
+
- **Very narrow (<50):** the essential summary only — status, task progress,
|
|
246
|
+
the current task, and the verification contract.
|
|
247
|
+
|
|
248
|
+
The renderer never emits a line wider than the terminal width: all alignment
|
|
249
|
+
and truncation is visible-width aware (ANSI colors, Unicode, and double-width
|
|
250
|
+
characters included). Golden tests assert `visibleWidth(line) <= width` for
|
|
251
|
+
every rendered line at 40, 50, 60, 80, 100, and 140 columns.
|
|
252
|
+
|
|
253
|
+
## `/goal-status`
|
|
254
|
+
|
|
255
|
+
`/goal-status` renders the same model as the widget.
|
|
256
|
+
|
|
257
|
+
- Standard mode: a static compact-dashboard rendering, current-task details,
|
|
258
|
+
recent activity, and the last audit result. No effective-settings noise.
|
|
259
|
+
- `/goal-status verbose`: goal id, revision, full objective, the complete
|
|
260
|
+
task tree with full evidence and contracts, recent ledger history, token
|
|
261
|
+
budget detail, pause/blocker detail, active and archived paths, the last
|
|
262
|
+
audit report, and effective settings with provenance.
|
|
263
|
+
|
|
264
|
+
## Migration behavior
|
|
265
|
+
|
|
266
|
+
The current-task focus is a new **optional** persisted field
|
|
267
|
+
(`currentTaskId`) on goal records.
|
|
268
|
+
|
|
269
|
+
- Legacy goal files load without it and are **never rewritten** just because
|
|
270
|
+
the field is absent.
|
|
271
|
+
- A persisted `currentTaskId` is accepted only when it references an existing
|
|
272
|
+
*pending* task; it is cleared when that task is completed, skipped, or
|
|
273
|
+
removed during normalization.
|
|
274
|
+
- Task-list restructuring preserves the current task only while its id
|
|
275
|
+
remains pending; otherwise the focus clears and the dashboard recomputes.
|
|
276
|
+
- For display only, the dashboard may infer the first pending task as the
|
|
277
|
+
current task and marks it as inferred.
|
|
278
|
+
|
|
279
|
+
## Compatibility
|
|
280
|
+
|
|
281
|
+
- Existing goal-file formats, archived goals, settings, slash commands, and
|
|
282
|
+
direct-goal behavior are preserved.
|
|
283
|
+
- The task-overlay shortcut is retained but now expands the unified
|
|
284
|
+
dashboard; the separate overlay registration is removed.
|
|
285
|
+
- Headless behavior remains functional without TUI rendering, and
|
|
286
|
+
audit-disabled completion remains explicit and distinct from approval.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auditor model/thinking selector helpers (ll01 cb6760b pattern).
|
|
3
|
+
*
|
|
4
|
+
* `/goal-settings` provider/model rows open a searchable selector: the
|
|
5
|
+
* current-session/default choice (which clears the explicit provider/model),
|
|
6
|
+
* authenticated models from the model registry (with a ✓ marker on the exact
|
|
7
|
+
* current selection), and an advanced manual `provider/model` entry. The
|
|
8
|
+
* thinking-level row offers Pi's seven levels or `(default)`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { GOAL_THINKING_LEVELS } from "./goal-settings.ts";
|
|
12
|
+
|
|
13
|
+
export type AuditorModelSummary = { provider: string; id: string; name?: string };
|
|
14
|
+
|
|
15
|
+
export type AuditorChoice =
|
|
16
|
+
| { kind: "default"; label: string }
|
|
17
|
+
| { kind: "model"; provider: string; model: string; label: string }
|
|
18
|
+
| { kind: "manual"; label: string };
|
|
19
|
+
|
|
20
|
+
export const AUDITOR_THINKING_LEVELS = GOAL_THINKING_LEVELS;
|
|
21
|
+
|
|
22
|
+
export function auditorModelLabel(model: AuditorModelSummary): string {
|
|
23
|
+
return `${model.provider}/${model.id}${model.name && model.name !== model.id ? ` — ${model.name}` : ""}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** `${provider}/${model}` key for the current configured model, if explicit. */
|
|
27
|
+
export function configuredAuditorModelKey(config: { provider?: string; model?: string }): string | undefined {
|
|
28
|
+
return config.provider && config.model ? `${config.provider}/${config.model}` : undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function buildAuditorModelChoices(
|
|
32
|
+
available: AuditorModelSummary[],
|
|
33
|
+
configured: string | undefined,
|
|
34
|
+
session: string | undefined,
|
|
35
|
+
): AuditorChoice[] {
|
|
36
|
+
const models = available.slice().sort((a, b) => auditorModelLabel(a).localeCompare(auditorModelLabel(b)));
|
|
37
|
+
return [
|
|
38
|
+
{
|
|
39
|
+
kind: "default",
|
|
40
|
+
label: `${configured ? " " : session ? "✓ " : " "}Current session / default${session ? ` (${session})` : " (system default)"}`,
|
|
41
|
+
},
|
|
42
|
+
...models.map((model) => {
|
|
43
|
+
const key = `${model.provider}/${model.id}`;
|
|
44
|
+
return {
|
|
45
|
+
kind: "model" as const,
|
|
46
|
+
provider: model.provider,
|
|
47
|
+
model: model.id,
|
|
48
|
+
label: `${configured === key ? "✓ " : " "}${auditorModelLabel(model)}`,
|
|
49
|
+
};
|
|
50
|
+
}),
|
|
51
|
+
{ kind: "manual", label: "✎ Enter provider/model manually (advanced)" },
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function filterAuditorModelChoices(choices: AuditorChoice[], filter: string): AuditorChoice[] {
|
|
56
|
+
const normalized = filter.trim().toLowerCase();
|
|
57
|
+
if (!normalized) return choices;
|
|
58
|
+
return choices.filter(
|
|
59
|
+
(choice) => choice.kind === "default" || choice.kind === "manual" || choice.label.toLowerCase().includes(normalized),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function parseManualAuditorModel(input: string): { provider: string; model: string } | { error: string } {
|
|
64
|
+
const value = input.trim();
|
|
65
|
+
const slash = value.indexOf("/");
|
|
66
|
+
if (slash <= 0 || slash === value.length - 1) {
|
|
67
|
+
return { error: "Auditor model must use provider/model format; unavailable models will fail clearly at audit time." };
|
|
68
|
+
}
|
|
69
|
+
return { provider: value.slice(0, slash), model: value.slice(slash + 1) };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function thinkingLevelChoices(current: string | undefined): string[] {
|
|
73
|
+
return ["(default)", ...AUDITOR_THINKING_LEVELS].map((level) => (level === current ? `✓ ${level}` : ` ${level}`));
|
|
74
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GoalAccounting — serialized, idempotent token/time accounting.
|
|
3
|
+
*
|
|
4
|
+
* One instance per extension. `begin(goalId)` starts a session for an active
|
|
5
|
+
* goal; `charge()` computes the elapsed seconds since the last charge and the
|
|
6
|
+
* completed-turn tokens, then advances the baseline so repeated calls never
|
|
7
|
+
* double-charge the same wall-clock interval. `liveSeconds()` is a read-only
|
|
8
|
+
* display helper that does not mutate state.
|
|
9
|
+
*
|
|
10
|
+
* Also exposes the token-budget helpers used by the runtime to detect the
|
|
11
|
+
* one-time `budget_limited` transition.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export interface AccountingCharge {
|
|
15
|
+
tokens: number;
|
|
16
|
+
seconds: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface BudgetLike {
|
|
20
|
+
tokenBudget?: number;
|
|
21
|
+
usage: { tokensUsed: number };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class GoalAccounting {
|
|
25
|
+
private activeGoalId: string | null = null;
|
|
26
|
+
private lastAccountedAt: number | null = null;
|
|
27
|
+
|
|
28
|
+
/** Start accounting for a goal id (or clear when null). */
|
|
29
|
+
begin(goalId: string | null): void {
|
|
30
|
+
this.activeGoalId = goalId;
|
|
31
|
+
this.lastAccountedAt = goalId ? Date.now() : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
clear(): void {
|
|
35
|
+
this.activeGoalId = null;
|
|
36
|
+
this.lastAccountedAt = null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get goalId(): string | null {
|
|
40
|
+
return this.activeGoalId;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** True when accounting is active for exactly this goal. */
|
|
44
|
+
isActiveFor(goalId: string): boolean {
|
|
45
|
+
return this.activeGoalId === goalId && this.lastAccountedAt !== null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Serialized idempotent charge: returns the tokens + whole elapsed seconds
|
|
50
|
+
* accumulated since the previous charge, then advances the baseline to
|
|
51
|
+
* `now`. Calling again immediately afterwards yields 0 seconds (never
|
|
52
|
+
* double-charges the same interval).
|
|
53
|
+
*/
|
|
54
|
+
charge(opts: { now?: number; completedTurnTokens?: number } = {}): AccountingCharge {
|
|
55
|
+
const now = opts.now ?? Date.now();
|
|
56
|
+
const elapsedSeconds = this.lastAccountedAt === null
|
|
57
|
+
? 0
|
|
58
|
+
: Math.max(0, Math.floor((now - this.lastAccountedAt) / 1000));
|
|
59
|
+
this.lastAccountedAt = now;
|
|
60
|
+
const tokens = Math.max(0, Math.trunc(opts.completedTurnTokens ?? 0));
|
|
61
|
+
return { tokens, seconds: elapsedSeconds };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Live elapsed whole seconds for display; does not advance the baseline. */
|
|
65
|
+
liveSeconds(now = Date.now()): number {
|
|
66
|
+
if (this.lastAccountedAt === null) return 0;
|
|
67
|
+
return Math.max(0, Math.floor((now - this.lastAccountedAt) / 1000));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Remaining token budget, or null when no budget is set. */
|
|
72
|
+
export function budgetRemaining(goal: BudgetLike): number | null {
|
|
73
|
+
if (typeof goal.tokenBudget !== "number" || !Number.isFinite(goal.tokenBudget) || goal.tokenBudget <= 0) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const used = Math.max(0, Math.floor(goal.usage.tokensUsed));
|
|
77
|
+
return Math.max(0, Math.floor(goal.tokenBudget - used));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** True when a budget is set and accounted usage has reached it. */
|
|
81
|
+
export function budgetReached(goal: BudgetLike): boolean {
|
|
82
|
+
const remaining = budgetRemaining(goal);
|
|
83
|
+
return remaining !== null && remaining === 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Token-budget description for display/steering text. */
|
|
87
|
+
export function budgetLine(goal: BudgetLike): string | null {
|
|
88
|
+
const remaining = budgetRemaining(goal);
|
|
89
|
+
if (remaining === null) return null;
|
|
90
|
+
const used = Math.floor(goal.usage.tokensUsed);
|
|
91
|
+
return `token budget: ${used}/${Math.floor(goal.tokenBudget ?? 0)} used, ${remaining} remaining`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Agent-facing accounting must never read as current context occupancy. */
|
|
95
|
+
export function modelBudgetLine(goal: BudgetLike): string | null {
|
|
96
|
+
const line = budgetLine(goal);
|
|
97
|
+
return line ? `Lifetime ${line} (spending cap, not context capacity)` : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface GoalContextUsage { tokens: number | null; contextWindow: number }
|
|
101
|
+
|
|
102
|
+
/** A point-in-time observation; older retained observations are historical. */
|
|
103
|
+
export function contextUsageLine(usage?: GoalContextUsage): string {
|
|
104
|
+
if (!usage || usage.tokens === null || !Number.isFinite(usage.tokens) || usage.tokens < 0
|
|
105
|
+
|| !Number.isFinite(usage.contextWindow) || usage.contextWindow <= 0) return "Context snapshot: unavailable";
|
|
106
|
+
return `Context snapshot: ${Math.round(usage.tokens)}/${Math.round(usage.contextWindow)} tokens (${Math.round(100 * usage.tokens / usage.contextWindow)}%)`;
|
|
107
|
+
}
|