@aragon-agent/cli 0.6.35 → 0.6.36
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/CHANGELOG.md +21 -0
- package/README.md +3173 -3071
- package/dist/agent/agent-mode.d.ts +20 -4
- package/dist/agent/agent-mode.js +20 -4
- package/dist/agent/controller.d.ts +38 -3
- package/dist/agent/controller.js +64 -4
- package/dist/agent/headless.js +31 -0
- package/dist/agent/reducer.d.ts +13 -2
- package/dist/agent/reducer.js +1 -0
- package/dist/cli.js +12 -0
- package/dist/commands/agents-command.d.ts +15 -0
- package/dist/commands/agents-command.js +27 -0
- package/dist/commands/builtins.js +131 -4
- package/dist/commands/perf.d.ts +11 -0
- package/dist/commands/perf.js +2 -1
- package/dist/commands/registry.d.ts +14 -0
- package/dist/config/app-paths.d.ts +15 -0
- package/dist/config/app-paths.js +33 -2
- package/dist/config/env.d.ts +4 -1
- package/dist/config/env.js +28 -2
- package/dist/config/load.d.ts +10 -0
- package/dist/config/load.js +8 -1
- package/dist/config/schema.d.ts +68 -0
- package/dist/config/schema.js +39 -0
- package/dist/config/store.js +12 -1
- package/dist/config/ui-state.d.ts +18 -0
- package/dist/config/ui-state.js +9 -0
- package/dist/exec/events.d.ts +25 -0
- package/dist/exec/events.js +4 -0
- package/dist/exec/index.js +16 -1
- package/dist/exec/runner.js +10 -0
- package/dist/proc/limits.d.ts +16 -0
- package/dist/proc/limits.js +16 -0
- package/dist/proc/supervisor.d.ts +20 -1
- package/dist/proc/supervisor.js +42 -14
- package/dist/prompts/command.d.ts +34 -0
- package/dist/prompts/command.js +237 -0
- package/dist/prompts/copy.d.ts +89 -0
- package/dist/prompts/copy.js +131 -0
- package/dist/prompts/fill.d.ts +51 -0
- package/dist/prompts/fill.js +46 -0
- package/dist/prompts/limits.d.ts +53 -0
- package/dist/prompts/limits.js +53 -0
- package/dist/prompts/normalize.d.ts +53 -0
- package/dist/prompts/normalize.js +190 -0
- package/dist/prompts/resolve.d.ts +31 -0
- package/dist/prompts/resolve.js +60 -0
- package/dist/prompts/search.d.ts +58 -0
- package/dist/prompts/search.js +146 -0
- package/dist/prompts/store.d.ts +86 -0
- package/dist/prompts/store.js +453 -0
- package/dist/prompts/types.d.ts +204 -0
- package/dist/prompts/types.js +13 -0
- package/dist/session/checkpoint.d.ts +8 -1
- package/dist/session/checkpoint.js +6 -0
- package/dist/session/persist.d.ts +19 -1
- package/dist/session/persist.js +13 -1
- package/dist/session/startup-session.d.ts +7 -2
- package/dist/session/startup-session.js +5 -2
- package/dist/session/store.js +3 -0
- package/dist/session/validate-session.js +64 -1
- package/dist/team/limits.d.ts +25 -0
- package/dist/team/limits.js +26 -0
- package/dist/team/runtime.js +27 -2
- package/dist/team/settled.d.ts +57 -0
- package/dist/team/settled.js +87 -0
- package/dist/team/subagent.js +48 -2
- package/dist/team/timeline.d.ts +74 -0
- package/dist/team/timeline.js +101 -0
- package/dist/team/types.d.ts +24 -1
- package/dist/todo/acceptance.d.ts +120 -0
- package/dist/todo/acceptance.js +399 -0
- package/dist/todo/follow-through.d.ts +48 -23
- package/dist/todo/follow-through.js +110 -44
- package/dist/todo/limits.d.ts +62 -1
- package/dist/todo/limits.js +62 -1
- package/dist/todo/panel-rows.d.ts +29 -39
- package/dist/todo/panel-rows.js +37 -83
- package/dist/todo/plan-sections.d.ts +60 -0
- package/dist/todo/plan-sections.js +108 -0
- package/dist/todo/prompt.d.ts +17 -12
- package/dist/todo/prompt.js +59 -40
- package/dist/todo/row-window.d.ts +51 -0
- package/dist/todo/row-window.js +107 -0
- package/dist/todo/store.d.ts +139 -31
- package/dist/todo/store.js +271 -73
- package/dist/todo/todo-tool.d.ts +33 -8
- package/dist/todo/todo-tool.js +186 -30
- package/dist/todo/types.d.ts +58 -0
- package/dist/ui/App.js +310 -46
- package/dist/ui/Composer.d.ts +6 -2
- package/dist/ui/Composer.js +2 -2
- package/dist/ui/Header.d.ts +9 -1
- package/dist/ui/Header.js +2 -2
- package/dist/ui/PromptInput.d.ts +21 -1
- package/dist/ui/PromptInput.js +21 -1
- package/dist/ui/ScrollbackShell.d.ts +19 -19
- package/dist/ui/ScrollbackShell.js +8 -1
- package/dist/ui/StatusBar.d.ts +5 -0
- package/dist/ui/StatusBar.js +1 -0
- package/dist/ui/TeamPanel.d.ts +26 -44
- package/dist/ui/TeamPanel.js +46 -135
- package/dist/ui/TodoPanel.d.ts +17 -14
- package/dist/ui/TodoPanel.js +146 -38
- package/dist/ui/Transcript.js +1 -1
- package/dist/ui/composer-input.d.ts +10 -0
- package/dist/ui/composer-input.js +57 -24
- package/dist/ui/entries/TodoCard.d.ts +26 -11
- package/dist/ui/entries/TodoCard.js +56 -15
- package/dist/ui/glyphs.d.ts +19 -0
- package/dist/ui/glyphs.js +4 -0
- package/dist/ui/layout/agents-board.d.ts +102 -0
- package/dist/ui/layout/agents-board.js +389 -0
- package/dist/ui/layout/cells.d.ts +61 -0
- package/dist/ui/layout/cells.js +156 -0
- package/dist/ui/layout/header-hints.d.ts +13 -3
- package/dist/ui/layout/header-hints.js +48 -14
- package/dist/ui/layout/status-detail-layout.js +9 -0
- package/dist/ui/layout/status-layout.d.ts +13 -0
- package/dist/ui/layout/team-row.d.ts +139 -0
- package/dist/ui/layout/team-row.js +246 -0
- package/dist/ui/layout/todo-layout.d.ts +38 -0
- package/dist/ui/layout/todo-layout.js +75 -0
- package/dist/ui/layout/virtual-window.js +23 -3
- package/dist/ui/overlays/AgentsBoardOverlay.d.ts +57 -0
- package/dist/ui/overlays/AgentsBoardOverlay.js +183 -0
- package/dist/ui/overlays/HelpOverlay.js +17 -2
- package/dist/ui/overlays/PromptEditorPage.d.ts +48 -0
- package/dist/ui/overlays/PromptEditorPage.js +227 -0
- package/dist/ui/overlays/PromptLibraryOverlay.d.ts +37 -0
- package/dist/ui/overlays/PromptLibraryOverlay.js +191 -0
- package/dist/ui/overlays/PromptPicker.d.ts +36 -0
- package/dist/ui/overlays/PromptPicker.js +129 -0
- package/dist/ui/overlays/PromptTextArea.d.ts +86 -0
- package/dist/ui/overlays/PromptTextArea.js +141 -0
- package/dist/ui/overlays/prompt-library-state.d.ts +162 -0
- package/dist/ui/overlays/prompt-library-state.js +335 -0
- package/dist/ui/overlays/prompt-picker-state.d.ts +71 -0
- package/dist/ui/overlays/prompt-picker-state.js +98 -0
- package/dist/ui/scrollback/band.js +14 -1
- package/dist/ui/team-status.d.ts +25 -0
- package/dist/ui/team-status.js +31 -3
- package/dist/ui/use-startup-notices.d.ts +35 -1
- package/dist/ui/use-startup-notices.js +29 -2
- package/dist/ui/use-wheel-routing.js +9 -1
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* follow-through — what happens when a run ends with unfinished
|
|
3
|
-
* (todo-plan-followthrough §3.2 / §3.3).
|
|
2
|
+
* follow-through — what happens when a run ends with unfinished work
|
|
3
|
+
* (todo-plan-followthrough §3.2 / §3.3; todo-acceptance-criteria §6.6).
|
|
4
4
|
*
|
|
5
5
|
* ASCII ONLY: `src/todo/**` is inside the glyph scanner's scope
|
|
6
6
|
* (`glyphs.test.ts`), and every string this module builds is user-visible.
|
|
@@ -14,9 +14,18 @@
|
|
|
14
14
|
* IT NEVER WRITES TO `TodoStore` (I-1 / C-3). Follow-through reads snapshots and
|
|
15
15
|
* produces a MESSAGE; the plan changes only when the model calls `todo_write`,
|
|
16
16
|
* which keeps the list a projection of what the model believes.
|
|
17
|
+
*
|
|
18
|
+
* "UNFINISHED WORK" HAS TWO HALVES SINCE ACCEPTANCE CRITERIA EXISTED (§6.6.1):
|
|
19
|
+
* steps still to do, and criteria still unchecked. Every exit below reads them
|
|
20
|
+
* from `outstandingOf`, and while ANY step is left the wording is byte-identical
|
|
21
|
+
* to the version without this feature (I-5) - because that is the phase in which
|
|
22
|
+
* the old sentences are already true. In the verification phase, where
|
|
23
|
+
* `total - doneCount` is always 0, saying "0 steps left" would be the exact
|
|
24
|
+
* opposite of the truth at the moment the user most wants reassurance.
|
|
17
25
|
*/
|
|
26
|
+
import { buildVerifyMessage, criteriaPhrase, outstandingOf } from './acceptance.js';
|
|
18
27
|
import { TODO_FOLLOW_LIMITS } from './limits.js';
|
|
19
|
-
/** The zero budget. A fresh App, a fresh headless run, and every cleared
|
|
28
|
+
/** The zero budget. A fresh App, a fresh headless run, and every cleared plan. */
|
|
20
29
|
export function emptyBudget() {
|
|
21
30
|
return { used: 0, noProgressStreak: 0, anchorTotal: 0, doneAtRunStart: 0 };
|
|
22
31
|
}
|
|
@@ -36,13 +45,35 @@ function legacyUnfinishedNotice(left) {
|
|
|
36
45
|
function advise(interactive, text) {
|
|
37
46
|
return interactive ? ` ${text}` : '';
|
|
38
47
|
}
|
|
48
|
+
/** `<n> acceptance criteria are unchecked` — singular and plural in one place. */
|
|
49
|
+
function uncheckedCriteria(count) {
|
|
50
|
+
return `${criteriaPhrase(count)} ${count === 1 ? 'is' : 'are'} unchecked`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The verification-phase notice for one exit (§6.6.1.1).
|
|
54
|
+
*
|
|
55
|
+
* ONE FUNCTION, SIX CALLERS, because the noun's number and the verb's number have
|
|
56
|
+
* to agree in the same sentence and six hand-written strings will not stay in
|
|
57
|
+
* agreement.
|
|
58
|
+
*/
|
|
59
|
+
function verificationNotice(checks, interactive, tail, advice) {
|
|
60
|
+
return `${uncheckedCriteria(checks)}${tail}${advise(interactive, advice)}`;
|
|
61
|
+
}
|
|
39
62
|
export function decideFollowThrough(input) {
|
|
40
63
|
const { mode, snapshot, runEnd, budget, interactive } = input;
|
|
41
|
-
// 1. Nothing is unfinished
|
|
42
|
-
//
|
|
43
|
-
|
|
64
|
+
// 1. Nothing is unfinished - INCLUDING the criteria (§6.6.1). "Every step is
|
|
65
|
+
// completed" stopped being the same claim as "there is nothing left" the
|
|
66
|
+
// moment a plan carried acceptance criteria; `beginUserTurn` already drops a
|
|
67
|
+
// fully-resolved plan at the next turn, so this branch must not pre-empt that
|
|
68
|
+
// feedback.
|
|
69
|
+
const outstanding = snapshot ? outstandingOf(snapshot) : { steps: 0, checks: 0 };
|
|
70
|
+
if (!snapshot || outstanding.steps + outstanding.checks === 0)
|
|
44
71
|
return { kind: 'none' };
|
|
45
|
-
const left =
|
|
72
|
+
const left = outstanding.steps;
|
|
73
|
+
// WHILE ANY STEP IS LEFT THE OLD SENTENCES ARE ALREADY TRUE, so they are reused
|
|
74
|
+
// verbatim (I-5) and only the verification phase gets new wording.
|
|
75
|
+
const verifying = left === 0 && outstanding.checks > 0;
|
|
76
|
+
const checks = outstanding.checks;
|
|
46
77
|
// 2. The user asked for silence and gets silence, INCLUDING no notice.
|
|
47
78
|
if (mode === 'off')
|
|
48
79
|
return { kind: 'none' };
|
|
@@ -51,25 +82,38 @@ export function decideFollowThrough(input) {
|
|
|
51
82
|
// continue it is worse.
|
|
52
83
|
if (runEnd.aborted)
|
|
53
84
|
return { kind: 'none' };
|
|
54
|
-
// 4. NEVER `continue`, IN ANY MODE (D-6). Auto-continuing into a model that
|
|
55
|
-
//
|
|
56
|
-
//
|
|
85
|
+
// 4. NEVER `continue`, IN ANY MODE (D-6). Auto-continuing into a model that just
|
|
86
|
+
// failed authentication or ran out of quota is the unbounded cost loop with
|
|
87
|
+
// none of the upside.
|
|
57
88
|
if (runEnd.errored) {
|
|
58
89
|
return {
|
|
59
90
|
kind: 'notify',
|
|
60
91
|
level: 'warn',
|
|
61
|
-
text:
|
|
62
|
-
|
|
92
|
+
text: verifying
|
|
93
|
+
? verificationNotice(checks, interactive, '; the run ended with an error.', '/todo continue to retry, /todo clear to drop it.')
|
|
94
|
+
: `${left} ${left === 1 ? 'step' : 'steps'} left; the run ended with an error.` +
|
|
95
|
+
advise(interactive, '/todo continue to retry, /todo clear to drop it.'),
|
|
63
96
|
};
|
|
64
97
|
}
|
|
65
|
-
// 5. The default. Byte-identical to round 1 in the TUI
|
|
98
|
+
// 5. The default. Byte-identical to round 1 in the TUI whenever steps remain
|
|
99
|
+
// (AC-1 / AC-44's reverse direction).
|
|
66
100
|
if (mode === 'notify') {
|
|
101
|
+
if (!verifying) {
|
|
102
|
+
return {
|
|
103
|
+
kind: 'notify',
|
|
104
|
+
level: 'info',
|
|
105
|
+
text: interactive
|
|
106
|
+
? legacyUnfinishedNotice(left)
|
|
107
|
+
: `${left} ${left === 1 ? 'step' : 'steps'} unfinished`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
67
110
|
return {
|
|
68
111
|
kind: 'notify',
|
|
69
112
|
level: 'info',
|
|
70
113
|
text: interactive
|
|
71
|
-
?
|
|
72
|
-
|
|
114
|
+
? `${uncheckedCriteria(checks)}. Use /todo continue to finish the check, ` +
|
|
115
|
+
'/todo clear to drop it.'
|
|
116
|
+
: `${criteriaPhrase(checks)} unchecked`,
|
|
73
117
|
};
|
|
74
118
|
}
|
|
75
119
|
// 6. The ceiling that holds when everything else is wrong.
|
|
@@ -77,33 +121,45 @@ export function decideFollowThrough(input) {
|
|
|
77
121
|
return {
|
|
78
122
|
kind: 'notify',
|
|
79
123
|
level: 'warn',
|
|
80
|
-
text:
|
|
81
|
-
|
|
82
|
-
|
|
124
|
+
text: verifying
|
|
125
|
+
? verificationNotice(checks, interactive, `; auto-continue has run ${TODO_FOLLOW_LIMITS.maxAutoContinuesPerList} times ` +
|
|
126
|
+
'for this plan.', '/todo continue to keep going.')
|
|
127
|
+
: `${left} ${left === 1 ? 'step' : 'steps'} left; auto-continue has run ` +
|
|
128
|
+
`${TODO_FOLLOW_LIMITS.maxAutoContinuesPerList} times for this plan.` +
|
|
129
|
+
advise(interactive, '/todo continue to keep going.'),
|
|
83
130
|
};
|
|
84
131
|
}
|
|
85
132
|
// 7. A model that has stopped engaging with its own plan hands back after one
|
|
86
133
|
// nudge rather than after twenty-five.
|
|
87
134
|
if (budget.noProgressStreak >= TODO_FOLLOW_LIMITS.maxNoProgressContinues) {
|
|
135
|
+
const attempts = TODO_FOLLOW_LIMITS.maxNoProgressContinues;
|
|
88
136
|
return {
|
|
89
137
|
kind: 'notify',
|
|
90
138
|
level: 'warn',
|
|
91
|
-
text:
|
|
92
|
-
|
|
93
|
-
|
|
139
|
+
text: verifying
|
|
140
|
+
? verificationNotice(checks, interactive, `; the last ${attempts} attempts checked none of them.`, '/todo continue to retry, or take over.')
|
|
141
|
+
: `${left} ${left === 1 ? 'step' : 'steps'} left; the last ` +
|
|
142
|
+
`${attempts} attempts completed none of them.` +
|
|
143
|
+
advise(interactive, '/todo continue to retry, or take over.'),
|
|
94
144
|
};
|
|
95
145
|
}
|
|
96
146
|
const graceMs = interactive ? TODO_FOLLOW_LIMITS.graceMs : 0;
|
|
147
|
+
const seconds = Math.round(graceMs / 1000);
|
|
97
148
|
return {
|
|
98
149
|
kind: 'continue',
|
|
99
150
|
message: buildContinuationMessage(snapshot),
|
|
100
151
|
graceMs,
|
|
101
152
|
// The seconds figure is DERIVED from the constant, never spelled again: a
|
|
102
153
|
// notice that promises 3 s while the timer waits 5 is worse than no notice.
|
|
103
|
-
notice:
|
|
104
|
-
?
|
|
105
|
-
|
|
106
|
-
|
|
154
|
+
notice: verifying
|
|
155
|
+
? interactive
|
|
156
|
+
? `Continuing with ${checks} unchecked acceptance ` +
|
|
157
|
+
`${checks === 1 ? 'criterion' : 'criteria'} in ${seconds}s - Esc to stop.`
|
|
158
|
+
: `continuing (${checks} acceptance ${checks === 1 ? 'criterion' : 'criteria'} unchecked)`
|
|
159
|
+
: interactive
|
|
160
|
+
? `Continuing with ${left} remaining ${left === 1 ? 'step' : 'steps'} in ` +
|
|
161
|
+
`${seconds}s - Esc to stop.`
|
|
162
|
+
: `continuing (${left} ${left === 1 ? 'step' : 'steps'} left)`,
|
|
107
163
|
};
|
|
108
164
|
}
|
|
109
165
|
/**
|
|
@@ -111,32 +167,36 @@ export function decideFollowThrough(input) {
|
|
|
111
167
|
* every `agent_end` (and on every resolved `prompt()` in headless).
|
|
112
168
|
*
|
|
113
169
|
* `used` is NOT incremented here — a decision that is never acted on costs
|
|
114
|
-
* nothing. Both fire points charge it themselves (I-5): the TUI's grace timer
|
|
115
|
-
*
|
|
116
|
-
*
|
|
170
|
+
* nothing. Both fire points charge it themselves (I-5): the TUI's grace timer and
|
|
171
|
+
* the headless loop, and BOTH are required. An uncharged counter in `-p` is an
|
|
172
|
+
* uncapped loop in the one environment with no human to press Esc (P1-2).
|
|
117
173
|
*/
|
|
118
174
|
export function advanceBudget(prev, snapshot, wasAutoContinuation) {
|
|
119
|
-
// THE ONLY PATH THAT CLEARS `used` (I-8). The
|
|
175
|
+
// THE ONLY PATH THAT CLEARS `used` (I-8). The plan is gone; so is its economy.
|
|
120
176
|
// It is not reachable by a model on its own: `TodoStore.write` leaves the
|
|
121
|
-
// previous
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
177
|
+
// previous plan untouched when a payload yields zero usable items, and the other
|
|
178
|
+
// three ways a plan disappears are `/todo clear`, `/reset` and `beginUserTurn`
|
|
179
|
+
// dropping a plan that is either finished (nothing to continue) or stale (which
|
|
180
|
+
// the streak rule beats to the punch, §3.5).
|
|
125
181
|
if (!snapshot)
|
|
126
182
|
return emptyBudget();
|
|
183
|
+
// The plan's identity is STEPS PLUS CRITERIA (§6.6.3): a re-plan that only
|
|
184
|
+
// changes the number of criteria is still a re-plan.
|
|
185
|
+
const anchor = snapshot.total + (snapshot.acceptance?.total ?? 0);
|
|
186
|
+
const progress = snapshot.doneCount + (snapshot.acceptance?.checkedCount ?? 0);
|
|
127
187
|
// A RE-PLAN BUYS FORGIVENESS, NOT A FRESH WALLET (D-16). Replanning is
|
|
128
188
|
// engagement rather than looping, which is why the streak is forgiven; zeroing
|
|
129
189
|
// `used` as well is what makes the hard ceiling unreachable, because a size
|
|
130
190
|
// change every turn would reset it every turn.
|
|
131
|
-
if (
|
|
191
|
+
if (anchor !== prev.anchorTotal) {
|
|
132
192
|
return {
|
|
133
193
|
used: prev.used,
|
|
134
194
|
noProgressStreak: 0,
|
|
135
|
-
anchorTotal:
|
|
136
|
-
doneAtRunStart:
|
|
195
|
+
anchorTotal: anchor,
|
|
196
|
+
doneAtRunStart: progress,
|
|
137
197
|
};
|
|
138
198
|
}
|
|
139
|
-
const progressed =
|
|
199
|
+
const progressed = progress > prev.doneAtRunStart;
|
|
140
200
|
// NOTE THE GUARD: a run the USER started never charges the streak, because the
|
|
141
201
|
// user asking a question mid-plan is not the model failing to advance it.
|
|
142
202
|
const noProgressStreak = progressed
|
|
@@ -150,7 +210,7 @@ export function advanceBudget(prev, snapshot, wasAutoContinuation) {
|
|
|
150
210
|
anchorTotal: prev.anchorTotal,
|
|
151
211
|
// Assigned in EVERY branch, which is what makes the next call's comparison
|
|
152
212
|
// meaningful.
|
|
153
|
-
doneAtRunStart:
|
|
213
|
+
doneAtRunStart: progress,
|
|
154
214
|
};
|
|
155
215
|
}
|
|
156
216
|
/**
|
|
@@ -158,14 +218,18 @@ export function advanceBudget(prev, snapshot, wasAutoContinuation) {
|
|
|
158
218
|
*
|
|
159
219
|
* IT ENUMERATES RATHER THAN REFERRING (D-11). Round 1 submitted the fixed string
|
|
160
220
|
* "Continue with the remaining todo items.", which is a reference to a
|
|
161
|
-
* `todo_write` call that in a long session may have scrolled far enough back
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
221
|
+
* `todo_write` call that in a long session may have scrolled far enough back that
|
|
222
|
+
* the model reconstructs it wrong — and reconstructing it wrong is SILENT, because
|
|
223
|
+
* whatever it writes next is a full replacement and becomes the new truth.
|
|
224
|
+
*
|
|
225
|
+
* IN THE VERIFICATION PHASE IT BRANCHES TO `buildVerifyMessage` (§6.6.2): with
|
|
226
|
+
* every step completed, the old body would emit "These steps are not done yet:"
|
|
227
|
+
* followed by nothing at all, which is what `/todo continue` and auto-continue
|
|
228
|
+
* would both submit at exactly the moment the plan needs its criteria checked.
|
|
165
229
|
*
|
|
166
230
|
* `content` and never `activeForm`: the imperative is what an instruction wants.
|
|
167
|
-
* Numbers are the item's 1-based position in the WHOLE
|
|
168
|
-
*
|
|
231
|
+
* Numbers are the item's 1-based position in the WHOLE plan, so "step 5" means the
|
|
232
|
+
* same thing here as it does in the rail. Completed items are omitted.
|
|
169
233
|
* `TODO_LIMITS.maxItems` bounds the whole message at roughly 1.8 kB.
|
|
170
234
|
*/
|
|
171
235
|
export function buildContinuationMessage(snapshot) {
|
|
@@ -173,6 +237,8 @@ export function buildContinuationMessage(snapshot) {
|
|
|
173
237
|
.map((item, index) => ({ item, index }))
|
|
174
238
|
.filter(({ item }) => item.status !== 'completed')
|
|
175
239
|
.map(({ item, index }) => `${index + 1}. ${item.content}`);
|
|
240
|
+
if (remaining.length === 0)
|
|
241
|
+
return buildVerifyMessage(snapshot);
|
|
176
242
|
return [
|
|
177
243
|
'Continue the plan. These steps are not done yet:',
|
|
178
244
|
...remaining,
|
package/dist/todo/limits.d.ts
CHANGED
|
@@ -87,5 +87,66 @@ export declare const TODO_FOLLOW_LIMITS: {
|
|
|
87
87
|
*
|
|
88
88
|
* NOT BUMPED BY follow-through (D-3): the model is never told about the
|
|
89
89
|
* mechanism, so the block is byte-identical to `bf6aeb1a`.
|
|
90
|
+
*
|
|
91
|
+
* BUMPED BY todo-acceptance-criteria (D-3 inverted): that feature CHANGES what
|
|
92
|
+
* the model reads, so the revision must be greppable from a behaviour report.
|
|
93
|
+
*/
|
|
94
|
+
export declare const TODO_BLOCK_VERSION = "v2-2026-10";
|
|
95
|
+
/**
|
|
96
|
+
* ACCEPT_LIMITS - the structural bounds of the acceptance table
|
|
97
|
+
* (todo-acceptance-criteria §6.1). STRUCTURAL, NOT POLICY, exactly like
|
|
98
|
+
* `TODO_LIMITS`: what a rail row, a transcript row and a model payload can
|
|
99
|
+
* physically carry.
|
|
90
100
|
*/
|
|
91
|
-
export declare const
|
|
101
|
+
export declare const ACCEPT_LIMITS: {
|
|
102
|
+
/**
|
|
103
|
+
* TWELVE, DELIBERATELY FEWER THAN `TODO_LIMITS.maxItems` (20). A plan with more
|
|
104
|
+
* than a dozen criteria is not a plan, it is a test suite - and `check` is where
|
|
105
|
+
* a suite belongs ("npm test"). The cap is semantic pressure: the model either
|
|
106
|
+
* merges criteria or admits it wrote a suite.
|
|
107
|
+
*/
|
|
108
|
+
readonly maxItems: 12;
|
|
109
|
+
/** `AcceptanceItem.content`, clamped (never rejected). */
|
|
110
|
+
readonly contentChars: 100;
|
|
111
|
+
/** `AcceptanceItem.check`, clamped. May be empty: a weak criterion, not an error. */
|
|
112
|
+
readonly checkChars: 120;
|
|
113
|
+
/** `AcceptanceItem.evidence`, clamped. */
|
|
114
|
+
readonly evidenceChars: 200;
|
|
115
|
+
/**
|
|
116
|
+
* A FRESH plan must declare at least this many usable criteria (§6.2.3 step 4).
|
|
117
|
+
* Gated on an EMPTY store AND on `todo.acceptance` being on: the requirement is
|
|
118
|
+
* "plan the criteria too", so it applies exactly where the capability does.
|
|
119
|
+
* Named `...Criteria`, not `...Items`, so it cannot be confused with
|
|
120
|
+
* `TODO_LIMITS.minFreshItems` (2) used two steps earlier.
|
|
121
|
+
*/
|
|
122
|
+
readonly minFreshCriteria: 1;
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* TODO_RESULT_LIMITS - the bound on what `todo_write` returns each turn
|
|
126
|
+
* (I-11 / §6.4.4). The acceptance paragraph pushes the text from about 700
|
|
127
|
+
* characters to roughly 1100, and every character is paid for on every call, so
|
|
128
|
+
* the ceiling is structural rather than advisory.
|
|
129
|
+
*/
|
|
130
|
+
export declare const TODO_RESULT_LIMITS: {
|
|
131
|
+
readonly maxChars: 1400;
|
|
132
|
+
/** Appended when the text had to be clipped, so a clipped answer says so. */
|
|
133
|
+
readonly truncationNote: "Note: result text truncated.";
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* VERIFY_MESSAGE_LIMITS - the CONSTRUCTIVE bound of `buildVerifyMessage`
|
|
137
|
+
* (§6.6.2 / AC-22), in the tradition of `buildChildDigest`: independent of how
|
|
138
|
+
* long the lists are, because every row it emits is clamped.
|
|
139
|
+
*
|
|
140
|
+
* The two numbers are ONE arithmetic, measured rather than estimated: at 5
|
|
141
|
+
* unchecked + 2 failed with `check` at 100 and `evidence` at 60 the worst case is
|
|
142
|
+
* 19 rows / 1699 characters, leaving 101 characters below `maxChars` and one row
|
|
143
|
+
* below `maxRows`.
|
|
144
|
+
*/
|
|
145
|
+
export declare const VERIFY_MESSAGE_LIMITS: {
|
|
146
|
+
readonly uncheckedItems: 5;
|
|
147
|
+
readonly failedItems: 2;
|
|
148
|
+
readonly checkChars: 100;
|
|
149
|
+
readonly evidenceChars: 60;
|
|
150
|
+
readonly maxChars: 1800;
|
|
151
|
+
readonly maxRows: 20;
|
|
152
|
+
};
|
package/dist/todo/limits.js
CHANGED
|
@@ -87,5 +87,66 @@ export const TODO_FOLLOW_LIMITS = {
|
|
|
87
87
|
*
|
|
88
88
|
* NOT BUMPED BY follow-through (D-3): the model is never told about the
|
|
89
89
|
* mechanism, so the block is byte-identical to `bf6aeb1a`.
|
|
90
|
+
*
|
|
91
|
+
* BUMPED BY todo-acceptance-criteria (D-3 inverted): that feature CHANGES what
|
|
92
|
+
* the model reads, so the revision must be greppable from a behaviour report.
|
|
93
|
+
*/
|
|
94
|
+
export const TODO_BLOCK_VERSION = 'v2-2026-10';
|
|
95
|
+
/**
|
|
96
|
+
* ACCEPT_LIMITS - the structural bounds of the acceptance table
|
|
97
|
+
* (todo-acceptance-criteria §6.1). STRUCTURAL, NOT POLICY, exactly like
|
|
98
|
+
* `TODO_LIMITS`: what a rail row, a transcript row and a model payload can
|
|
99
|
+
* physically carry.
|
|
90
100
|
*/
|
|
91
|
-
export const
|
|
101
|
+
export const ACCEPT_LIMITS = {
|
|
102
|
+
/**
|
|
103
|
+
* TWELVE, DELIBERATELY FEWER THAN `TODO_LIMITS.maxItems` (20). A plan with more
|
|
104
|
+
* than a dozen criteria is not a plan, it is a test suite - and `check` is where
|
|
105
|
+
* a suite belongs ("npm test"). The cap is semantic pressure: the model either
|
|
106
|
+
* merges criteria or admits it wrote a suite.
|
|
107
|
+
*/
|
|
108
|
+
maxItems: 12,
|
|
109
|
+
/** `AcceptanceItem.content`, clamped (never rejected). */
|
|
110
|
+
contentChars: 100,
|
|
111
|
+
/** `AcceptanceItem.check`, clamped. May be empty: a weak criterion, not an error. */
|
|
112
|
+
checkChars: 120,
|
|
113
|
+
/** `AcceptanceItem.evidence`, clamped. */
|
|
114
|
+
evidenceChars: 200,
|
|
115
|
+
/**
|
|
116
|
+
* A FRESH plan must declare at least this many usable criteria (§6.2.3 step 4).
|
|
117
|
+
* Gated on an EMPTY store AND on `todo.acceptance` being on: the requirement is
|
|
118
|
+
* "plan the criteria too", so it applies exactly where the capability does.
|
|
119
|
+
* Named `...Criteria`, not `...Items`, so it cannot be confused with
|
|
120
|
+
* `TODO_LIMITS.minFreshItems` (2) used two steps earlier.
|
|
121
|
+
*/
|
|
122
|
+
minFreshCriteria: 1,
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* TODO_RESULT_LIMITS - the bound on what `todo_write` returns each turn
|
|
126
|
+
* (I-11 / §6.4.4). The acceptance paragraph pushes the text from about 700
|
|
127
|
+
* characters to roughly 1100, and every character is paid for on every call, so
|
|
128
|
+
* the ceiling is structural rather than advisory.
|
|
129
|
+
*/
|
|
130
|
+
export const TODO_RESULT_LIMITS = {
|
|
131
|
+
maxChars: 1400,
|
|
132
|
+
/** Appended when the text had to be clipped, so a clipped answer says so. */
|
|
133
|
+
truncationNote: 'Note: result text truncated.',
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* VERIFY_MESSAGE_LIMITS - the CONSTRUCTIVE bound of `buildVerifyMessage`
|
|
137
|
+
* (§6.6.2 / AC-22), in the tradition of `buildChildDigest`: independent of how
|
|
138
|
+
* long the lists are, because every row it emits is clamped.
|
|
139
|
+
*
|
|
140
|
+
* The two numbers are ONE arithmetic, measured rather than estimated: at 5
|
|
141
|
+
* unchecked + 2 failed with `check` at 100 and `evidence` at 60 the worst case is
|
|
142
|
+
* 19 rows / 1699 characters, leaving 101 characters below `maxChars` and one row
|
|
143
|
+
* below `maxRows`.
|
|
144
|
+
*/
|
|
145
|
+
export const VERIFY_MESSAGE_LIMITS = {
|
|
146
|
+
uncheckedItems: 5,
|
|
147
|
+
failedItems: 2,
|
|
148
|
+
checkChars: 100,
|
|
149
|
+
evidenceChars: 60,
|
|
150
|
+
maxChars: 1800,
|
|
151
|
+
maxRows: 20,
|
|
152
|
+
};
|
|
@@ -1,55 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Which rows the rail shows (todo-plan-execution §3.10).
|
|
3
3
|
*
|
|
4
4
|
* ASCII ONLY: `src/todo/**` is inside the glyph scanner's scope.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* and reports what is off each end.
|
|
13
|
-
*
|
|
14
|
-
* THE DIRECT DESCENDANT OF `selectPanelRows`'s F-2 LESSON: the team panel
|
|
15
|
-
* originally took the first five children by ARRAY INDEX, which showed the five
|
|
16
|
-
* that finished FIRST and hid everything still working. Here the equivalent
|
|
17
|
-
* mistake is `slice(0, maxRows)`, which on a 20-item plan shows steps 1-8 - all
|
|
18
|
-
* completed - and hides the one the user is actually watching.
|
|
6
|
+
* THE ALGORITHM LIVES IN `row-window.ts` since todo-acceptance-criteria §6.9.4:
|
|
7
|
+
* the acceptance section needs the same anchored-window fixpoint over a different
|
|
8
|
+
* item shape, and two copies of it would be two places for the "anchor must stay
|
|
9
|
+
* visible" rule to rot. The three exports below keep their EXACT signatures and
|
|
10
|
+
* their field-for-field output (AC-13), so every existing caller and test is
|
|
11
|
+
* unaffected.
|
|
19
12
|
*/
|
|
20
|
-
import type { TodoItem } from './types.js';
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
index: number;
|
|
25
|
-
}[];
|
|
26
|
-
hiddenAbove: number;
|
|
27
|
-
hiddenBelow: number;
|
|
28
|
-
}
|
|
13
|
+
import type { AcceptanceItem, TodoItem } from './types.js';
|
|
14
|
+
import { type RowWindow } from './row-window.js';
|
|
15
|
+
export type TodoRowSelection = RowWindow<TodoItem>;
|
|
16
|
+
export type AcceptanceRowSelection = RowWindow<AcceptanceItem>;
|
|
29
17
|
/**
|
|
30
|
-
* The row the window is built around: the item in progress, or the first
|
|
18
|
+
* The row the todo window is built around: the item in progress, or the first
|
|
31
19
|
* unfinished one, or the last item when everything is done.
|
|
32
20
|
*
|
|
33
21
|
* `normalizeTodos` guarantees an `in_progress` item whenever one is not
|
|
34
|
-
* completed (I-3), so the second branch is a belt-and-braces path for a list
|
|
35
|
-
*
|
|
22
|
+
* completed (I-3), so the second branch is a belt-and-braces path for a list this
|
|
23
|
+
* module was handed without going through the store.
|
|
24
|
+
*/
|
|
25
|
+
export declare function todoAnchorIndex(items: readonly TodoItem[]): number;
|
|
26
|
+
/**
|
|
27
|
+
* The row the acceptance window is built around (§6.9.4): the criterion being
|
|
28
|
+
* checked, or the first unchecked one, or the last row when every verdict is in.
|
|
29
|
+
*
|
|
30
|
+
* THE `in_progress` BRANCH IS THE WHOLE POINT. Without it the anchor would be
|
|
31
|
+
* "first pending", so re-sending the list while checking criterion 3 would snap
|
|
32
|
+
* the rail back to criterion 1 - the F-2 lesson again, one feature later.
|
|
36
33
|
*/
|
|
37
|
-
export declare function
|
|
34
|
+
export declare function acceptanceAnchorIndex(items: readonly AcceptanceItem[]): number;
|
|
38
35
|
/**
|
|
39
36
|
* @param maxRows Rows available for the item rows AND the two overflow markers
|
|
40
37
|
* together. The shared layout subtracts actual team/menu rows.
|
|
41
|
-
*
|
|
42
|
-
* THE ANCHOR IS ALWAYS VISIBLE, and that beats the row bound when the two
|
|
43
|
-
* conflict. They can only conflict below `maxRows` 3 with overflow on BOTH
|
|
44
|
-
* sides — one item plus two markers is three rows — where this returns the
|
|
45
|
-
* anchor and lets the caller's `overflow="hidden"` clip a marker. Hiding the
|
|
46
|
-
* one row the user is watching in order to keep a `+N above` would be the wrong
|
|
47
|
-
* trade. The full rail has at least four item rows. The compact rail uses
|
|
48
|
-
* `selectCompactTodoRows` with a single merged footer instead.
|
|
49
38
|
*/
|
|
50
39
|
export declare function selectTodoRows(items: TodoItem[], maxRows: number): TodoRowSelection;
|
|
51
|
-
/** Select an anchored contiguous window without reserving overflow-marker rows.
|
|
52
|
-
* maxItemRows is a row count; invalid/nonpositive values return an empty window
|
|
53
|
-
* with every item counted below it. Does not mutate the list or throw.
|
|
54
|
-
*/
|
|
40
|
+
/** Select an anchored contiguous window without reserving overflow-marker rows. */
|
|
55
41
|
export declare function selectCompactTodoRows(items: TodoItem[], maxItemRows: number): TodoRowSelection;
|
|
42
|
+
/** The acceptance section's window; same discipline, same anchors, other shape. */
|
|
43
|
+
export declare function selectAcceptanceRows(items: AcceptanceItem[], maxRows: number): AcceptanceRowSelection;
|
|
44
|
+
/** The acceptance section's compact window (summary rung uses this). */
|
|
45
|
+
export declare function selectCompactAcceptanceRows(items: AcceptanceItem[], maxRows: number): AcceptanceRowSelection;
|
package/dist/todo/panel-rows.js
CHANGED
|
@@ -1,29 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Which rows the rail shows (todo-plan-execution §3.10).
|
|
3
3
|
*
|
|
4
4
|
* ASCII ONLY: `src/todo/**` is inside the glyph scanner's scope.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* and reports what is off each end.
|
|
13
|
-
*
|
|
14
|
-
* THE DIRECT DESCENDANT OF `selectPanelRows`'s F-2 LESSON: the team panel
|
|
15
|
-
* originally took the first five children by ARRAY INDEX, which showed the five
|
|
16
|
-
* that finished FIRST and hid everything still working. Here the equivalent
|
|
17
|
-
* mistake is `slice(0, maxRows)`, which on a 20-item plan shows steps 1-8 - all
|
|
18
|
-
* completed - and hides the one the user is actually watching.
|
|
6
|
+
* THE ALGORITHM LIVES IN `row-window.ts` since todo-acceptance-criteria §6.9.4:
|
|
7
|
+
* the acceptance section needs the same anchored-window fixpoint over a different
|
|
8
|
+
* item shape, and two copies of it would be two places for the "anchor must stay
|
|
9
|
+
* visible" rule to rot. The three exports below keep their EXACT signatures and
|
|
10
|
+
* their field-for-field output (AC-13), so every existing caller and test is
|
|
11
|
+
* unaffected.
|
|
19
12
|
*/
|
|
13
|
+
import { selectCompactRowsAround, selectRowsAround } from './row-window.js';
|
|
20
14
|
/**
|
|
21
|
-
* The row the window is built around: the item in progress, or the first
|
|
15
|
+
* The row the todo window is built around: the item in progress, or the first
|
|
22
16
|
* unfinished one, or the last item when everything is done.
|
|
23
17
|
*
|
|
24
18
|
* `normalizeTodos` guarantees an `in_progress` item whenever one is not
|
|
25
|
-
* completed (I-3), so the second branch is a belt-and-braces path for a list
|
|
26
|
-
*
|
|
19
|
+
* completed (I-3), so the second branch is a belt-and-braces path for a list this
|
|
20
|
+
* module was handed without going through the store.
|
|
27
21
|
*/
|
|
28
22
|
export function todoAnchorIndex(items) {
|
|
29
23
|
if (items.length === 0)
|
|
@@ -37,80 +31,40 @@ export function todoAnchorIndex(items) {
|
|
|
37
31
|
return items.length - 1;
|
|
38
32
|
}
|
|
39
33
|
/**
|
|
40
|
-
*
|
|
34
|
+
* The row the acceptance window is built around (§6.9.4): the criterion being
|
|
35
|
+
* checked, or the first unchecked one, or the last row when every verdict is in.
|
|
41
36
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
37
|
+
* THE `in_progress` BRANCH IS THE WHOLE POINT. Without it the anchor would be
|
|
38
|
+
* "first pending", so re-sending the list while checking criterion 3 would snap
|
|
39
|
+
* the rail back to criterion 1 - the F-2 lesson again, one feature later.
|
|
45
40
|
*/
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
return { start, end };
|
|
41
|
+
export function acceptanceAnchorIndex(items) {
|
|
42
|
+
if (items.length === 0)
|
|
43
|
+
return 0;
|
|
44
|
+
const active = items.findIndex((item) => item.status === 'in_progress');
|
|
45
|
+
if (active >= 0)
|
|
46
|
+
return active;
|
|
47
|
+
const unchecked = items.findIndex((item) => item.status === 'pending' || item.status === 'in_progress');
|
|
48
|
+
if (unchecked >= 0)
|
|
49
|
+
return unchecked;
|
|
50
|
+
return items.length - 1;
|
|
58
51
|
}
|
|
59
52
|
/**
|
|
60
53
|
* @param maxRows Rows available for the item rows AND the two overflow markers
|
|
61
54
|
* together. The shared layout subtracts actual team/menu rows.
|
|
62
|
-
*
|
|
63
|
-
* THE ANCHOR IS ALWAYS VISIBLE, and that beats the row bound when the two
|
|
64
|
-
* conflict. They can only conflict below `maxRows` 3 with overflow on BOTH
|
|
65
|
-
* sides — one item plus two markers is three rows — where this returns the
|
|
66
|
-
* anchor and lets the caller's `overflow="hidden"` clip a marker. Hiding the
|
|
67
|
-
* one row the user is watching in order to keep a `+N above` would be the wrong
|
|
68
|
-
* trade. The full rail has at least four item rows. The compact rail uses
|
|
69
|
-
* `selectCompactTodoRows` with a single merged footer instead.
|
|
70
55
|
*/
|
|
71
56
|
export function selectTodoRows(items, maxRows) {
|
|
72
|
-
|
|
73
|
-
const anchor = todoAnchorIndex(items);
|
|
74
|
-
if (count === 0)
|
|
75
|
-
return { visible: [], hiddenAbove: 0, hiddenBelow: 0 };
|
|
76
|
-
if (maxRows <= 0) {
|
|
77
|
-
return { visible: [], hiddenAbove: anchor, hiddenBelow: count - 1 - anchor };
|
|
78
|
-
}
|
|
79
|
-
// The marker rows and the window depend on each other: a `+N above` row costs
|
|
80
|
-
// a slot, which can shrink the window, which can create a `+N below`. Two
|
|
81
|
-
// passes reach the fixpoint for every input this can be handed; the loop bound
|
|
82
|
-
// is a backstop, not a live case.
|
|
83
|
-
let reserved = 0;
|
|
84
|
-
let start = 0;
|
|
85
|
-
let end = count;
|
|
86
|
-
for (let pass = 0; pass < 3; pass += 1) {
|
|
87
|
-
const slots = Math.max(1, maxRows - reserved);
|
|
88
|
-
const win = windowFor(count, anchor, slots);
|
|
89
|
-
start = win.start;
|
|
90
|
-
end = win.end;
|
|
91
|
-
const need = (start > 0 ? 1 : 0) + (end < count ? 1 : 0);
|
|
92
|
-
if (need === reserved)
|
|
93
|
-
break;
|
|
94
|
-
reserved = need;
|
|
95
|
-
}
|
|
96
|
-
const visible = [];
|
|
97
|
-
for (let i = start; i < end; i += 1)
|
|
98
|
-
visible.push({ item: items[i], index: i });
|
|
99
|
-
return { visible, hiddenAbove: start, hiddenBelow: count - end };
|
|
57
|
+
return selectRowsAround(items, maxRows, todoAnchorIndex);
|
|
100
58
|
}
|
|
101
|
-
/** Select an anchored contiguous window without reserving overflow-marker rows.
|
|
102
|
-
* maxItemRows is a row count; invalid/nonpositive values return an empty window
|
|
103
|
-
* with every item counted below it. Does not mutate the list or throw.
|
|
104
|
-
*/
|
|
59
|
+
/** Select an anchored contiguous window without reserving overflow-marker rows. */
|
|
105
60
|
export function selectCompactTodoRows(items, maxItemRows) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
};
|
|
61
|
+
return selectCompactRowsAround(items, maxItemRows, todoAnchorIndex);
|
|
62
|
+
}
|
|
63
|
+
/** The acceptance section's window; same discipline, same anchors, other shape. */
|
|
64
|
+
export function selectAcceptanceRows(items, maxRows) {
|
|
65
|
+
return selectRowsAround(items, maxRows, acceptanceAnchorIndex);
|
|
66
|
+
}
|
|
67
|
+
/** The acceptance section's compact window (summary rung uses this). */
|
|
68
|
+
export function selectCompactAcceptanceRows(items, maxRows) {
|
|
69
|
+
return selectCompactRowsAround(items, maxRows, acceptanceAnchorIndex);
|
|
116
70
|
}
|