@mafdet/cli 0.4.5 → 0.4.7
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 +54 -0
- package/README.md +3 -3
- package/README.zh-CN.md +1 -1
- package/dist/agent/command-policy.d.ts.map +1 -1
- package/dist/agent/command-policy.js +17 -13
- package/dist/agent/context-budget.d.ts +4 -1
- package/dist/agent/context-budget.d.ts.map +1 -1
- package/dist/agent/context-budget.js +45 -10
- package/dist/agent/loop.d.ts +7 -0
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +14 -1
- package/dist/agent/session.d.ts +12 -0
- package/dist/agent/session.d.ts.map +1 -1
- package/dist/agent/session.js +8 -2
- package/dist/api/models.d.ts.map +1 -1
- package/dist/api/models.js +28 -2
- package/dist/api/sse-parser.d.ts.map +1 -1
- package/dist/api/sse-parser.js +11 -2
- package/dist/commands/agent.d.ts +8 -0
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +21 -5
- package/dist/commands/sessions.js +11 -2
- package/dist/output/money.d.ts +9 -0
- package/dist/output/money.d.ts.map +1 -1
- package/dist/output/money.js +14 -0
- package/dist/tui/agent-surface.d.ts.map +1 -1
- package/dist/tui/agent-surface.js +8 -11
- package/dist/tui/session-centre.d.ts.map +1 -1
- package/dist/tui/session-centre.js +5 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,60 @@ The version is the contract for the JSON output shapes and the exit codes.
|
|
|
6
6
|
Being 0.x, semver still permits a break in a minor bump; the intent is not to
|
|
7
7
|
use that latitude, and the promise becomes binding at 1.0.0.
|
|
8
8
|
|
|
9
|
+
## 0.4.7 - 2026-08-12
|
|
10
|
+
|
|
11
|
+
0.4.6 shipped without the fix it was published for.
|
|
12
|
+
|
|
13
|
+
Its headline change was that an interactive `mafdet agent` records your
|
|
14
|
+
questions and not only the model's replies. The edit that connected the
|
|
15
|
+
recorder to the session loop matched nothing when it was applied, and the
|
|
16
|
+
version went out with the old behaviour: typecheck passed, the whole suite
|
|
17
|
+
passed, and the unit test for it passed too, because that test drives the
|
|
18
|
+
session loop directly and never touches the command that was supposed to call
|
|
19
|
+
it. A real interactive run found it a few minutes later.
|
|
20
|
+
|
|
21
|
+
This release contains the wiring, and a test that goes through the command
|
|
22
|
+
rather than around it - verified to fail when the wiring is removed. If you
|
|
23
|
+
installed 0.4.6, upgrade: everything else in it was real, but that one was not.
|
|
24
|
+
|
|
25
|
+
## 0.4.6 - 2026-08-12
|
|
26
|
+
|
|
27
|
+
A third audit round. Two of its findings were things the previous round's
|
|
28
|
+
notes recorded as fixed, and the pattern is worth naming: a partial fix logged
|
|
29
|
+
as complete is not looked at again.
|
|
30
|
+
|
|
31
|
+
**An interactive `mafdet agent` now records your questions.** It saved the
|
|
32
|
+
model's replies, the tool runs and the usage, and none of the prompts - so
|
|
33
|
+
resuming such a session could not continue a conversation that was only half
|
|
34
|
+
there. The TUI wrapped its own reader to record them and the command surface
|
|
35
|
+
did not; recording now happens in the session loop, where neither can forget.
|
|
36
|
+
|
|
37
|
+
**`--max-cost-usd` refuses before spending, not after.** When the catalogue
|
|
38
|
+
publishes no price for a model, the run used to make one billable request and
|
|
39
|
+
discover the problem from the response. Whether a price exists is known before
|
|
40
|
+
anything is sent.
|
|
41
|
+
|
|
42
|
+
**A run nobody could price no longer reports `$0.00`.** The command summary,
|
|
43
|
+
the TUI summary, `/cost` and the JSON report all showed zero for a run whose
|
|
44
|
+
model had no published price; they now say how many requests the figure does
|
|
45
|
+
not cover, as `sessions usage` already did.
|
|
46
|
+
|
|
47
|
+
**Context compaction accounts for what cannot shrink.** The estimate assumed
|
|
48
|
+
tokens fall in proportion to messages, but the system prompt and tool schemas
|
|
49
|
+
are re-sent every turn - so it could decide a history had come inside the
|
|
50
|
+
window when only the removable part had.
|
|
51
|
+
|
|
52
|
+
**Model catalogue text is sanitised where it enters.** Terminal escape
|
|
53
|
+
sequences were being stripped sink by sink, and the model picker was still
|
|
54
|
+
missing after two rounds; cleaning the catalogue on arrival leaves nothing
|
|
55
|
+
downstream to forget. Fixed with it: stripping a sequence no longer eats the
|
|
56
|
+
text that follows it, which an 8-bit OSC terminated by BEL used to do.
|
|
57
|
+
|
|
58
|
+
Also: the stream's total deadline actually bounds the wait instead of allowing
|
|
59
|
+
one more idle period; compaction reports how many turns it dropped rather than
|
|
60
|
+
always one; the session picker and the per-model and per-key JSON carry the
|
|
61
|
+
unpriced count.
|
|
62
|
+
|
|
9
63
|
## 0.4.5 - 2026-08-12
|
|
10
64
|
|
|
11
65
|
A second audit round, and the security fix in it is the important one.
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The official Mafdet AI command line: model calls, account queries, and a
|
|
4
4
|
permissioned local agent.
|
|
5
5
|
|
|
6
|
-
> **0.4.
|
|
6
|
+
> **0.4.7.** 中文版见 [README.zh-CN.md](./README.zh-CN.md). The CLI never
|
|
7
7
|
> advertises a command it cannot serve, so `mafdet help` always lists exactly
|
|
8
8
|
> what works.
|
|
9
9
|
>
|
|
@@ -23,8 +23,8 @@ Or build it from this repository:
|
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
pnpm --filter @mafdet/cli build
|
|
26
|
-
cd packages/cli && npm pack # -> mafdet-cli-0.4.
|
|
27
|
-
npm install -g ./mafdet-cli-0.4.
|
|
26
|
+
cd packages/cli && npm pack # -> mafdet-cli-0.4.7.tgz
|
|
27
|
+
npm install -g ./mafdet-cli-0.4.7.tgz
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
## The interactive session
|
package/README.zh-CN.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-policy.d.ts","sourceRoot":"","sources":["../../src/agent/command-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAeH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CA4BlD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,SAAS,MAAM,EAAO,GAChC,cAAc,CAwChB;AAmBD,+EAA+E;AAC/E,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"command-policy.d.ts","sourceRoot":"","sources":["../../src/agent/command-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAeH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CA4BlD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,SAAS,MAAM,EAAO,GAChC,cAAc,CAwChB;AAmBD,+EAA+E;AAC/E,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA4B9C"}
|
|
@@ -119,24 +119,28 @@ function matchesAllowlist(argv, allowlist) {
|
|
|
119
119
|
/** Terminal control sequences, stripped before any output is shown or sent. */
|
|
120
120
|
export function stripAnsi(text) {
|
|
121
121
|
return (text
|
|
122
|
-
//
|
|
122
|
+
// Whole sequences first, in both their 7-bit and 8-bit forms, and only
|
|
123
|
+
// then the leftover control characters.
|
|
124
|
+
//
|
|
125
|
+
// The order is load-bearing and was wrong once: removing C0 controls
|
|
126
|
+
// before parsing the 8-bit OSC took away the BEL that terminates it, so
|
|
127
|
+
// the pattern ran to the end of the string and swallowed the ordinary
|
|
128
|
+
// text after the sequence. Failing safe, but destroying output.
|
|
129
|
+
//
|
|
130
|
+
// CSI: colours, cursor moves, screen clears.
|
|
123
131
|
.replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, "")
|
|
124
|
-
|
|
125
|
-
// the
|
|
126
|
-
|
|
127
|
-
//
|
|
132
|
+
.replace(/\u009b[0-?]*[ -/]*[@-~]/g, "")
|
|
133
|
+
// OSC: window titles and the like. The payload goes with the
|
|
134
|
+
// introducer - stripping only the introducer would leave "0;retitled"
|
|
135
|
+
// sitting in the output. Terminated by BEL or by ST in either form.
|
|
136
|
+
.replace(/\u001b\][^\u0007\u001b\u009c]*(?:\u0007|\u001b\\|\u009c)?/g, "")
|
|
137
|
+
.replace(/\u009d[^\u0007\u001b\u009c]*(?:\u0007|\u001b\\|\u009c)?/g, "")
|
|
138
|
+
// Other single-character escapes.
|
|
128
139
|
.replace(/\u001b[@-Z\\-_]/g, "")
|
|
129
140
|
// Remaining C0 controls. Tab, newline and carriage return survive; the
|
|
130
141
|
// rest can retitle a window, move the cursor or hide what follows, and a
|
|
131
142
|
// model's output is not a trusted source of terminal instructions.
|
|
132
143
|
.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "")
|
|
133
|
-
//
|
|
134
|
-
// mode reads U+009B as CSI and U+009D as OSC - identical instructions to
|
|
135
|
-
// the ESC forms above, with no ESC to match on. Taken as whole
|
|
136
|
-
// sequences, so the payload goes with the introducer rather than being
|
|
137
|
-
// left behind as visible text.
|
|
138
|
-
.replace(/\u009b[0-?]*[ -/]*[@-~]/g, "")
|
|
139
|
-
.replace(/\u009d[^\u0007\u009c]*(?:\u0007|\u009c)?/g, "")
|
|
140
|
-
// Any remaining C1, including NEL (U+0085) and the lone introducers.
|
|
144
|
+
// Remaining C1, including NEL (U+0085) and any lone introducer.
|
|
141
145
|
.replace(/[\u0080-\u009f]/g, ""));
|
|
142
146
|
}
|
|
@@ -47,7 +47,10 @@ export declare function pruneToolExchanges(history: ChatMessage[], keepRecent?:
|
|
|
47
47
|
* unanswered: CLI-014's rule holds by construction. The system prompt and the
|
|
48
48
|
* first user message - the goal - always stay.
|
|
49
49
|
*/
|
|
50
|
-
export declare function dropOldestTurns(history: ChatMessage[], keepTurns: number):
|
|
50
|
+
export declare function dropOldestTurns(history: ChatMessage[], keepTurns: number): {
|
|
51
|
+
removed: number;
|
|
52
|
+
turns: number;
|
|
53
|
+
};
|
|
51
54
|
export interface CompactionResult extends PruneResult {
|
|
52
55
|
/** Whole turns dropped, when exchanges were not enough. */
|
|
53
56
|
readonly turns: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget.d.ts","sourceRoot":"","sources":["../../src/agent/context-budget.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,OAAO,CAAC;AAE/B,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,6EAA6E;AAC7E,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAKT;AAED,MAAM,WAAW,WAAW;IAC1B,uCAAuC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,uCAAuC;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAsCD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,WAAW,EAAE,EACtB,UAAU,GAAE,MAA8B,GACzC,WAAW,CAkBb;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,WAAW,EAAE,EACtB,SAAS,EAAE,MAAM,GAChB,MAAM,
|
|
1
|
+
{"version":3,"file":"context-budget.d.ts","sourceRoot":"","sources":["../../src/agent/context-budget.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,OAAO,CAAC;AAE/B,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,6EAA6E;AAC7E,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAKT;AAED,MAAM,WAAW,WAAW;IAC1B,uCAAuC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,uCAAuC;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAsCD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,WAAW,EAAE,EACtB,UAAU,GAAE,MAA8B,GACzC,WAAW,CAkBb;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,WAAW,EAAE,EACtB,SAAS,EAAE,MAAM,GAChB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA0BpC;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,2EAA2E;IAC3E,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,WAAW,EAAE,EACtB,UAAU,GAAE,MAA8B,GACzC,gBAAgB,CASlB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,WAAW,EAAE,EACtB,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,gBAAgB,EAAE,MAAM,EACxB,UAAU,GAAE,MAA8B,GACzC,gBAAgB,CAoClB"}
|
|
@@ -125,10 +125,10 @@ export function dropOldestTurns(history, keepTurns) {
|
|
|
125
125
|
// The first user message is the goal and is not a droppable turn, so there
|
|
126
126
|
// have to be more than `keepTurns` turns after it before anything can go.
|
|
127
127
|
if (userAt.length <= keepTurns + 1)
|
|
128
|
-
return 0;
|
|
128
|
+
return { removed: 0, turns: 0 };
|
|
129
129
|
const cut = userAt[userAt.length - keepTurns];
|
|
130
130
|
if (cut === undefined)
|
|
131
|
-
return 0;
|
|
131
|
+
return { removed: 0, turns: 0 };
|
|
132
132
|
const kept = [];
|
|
133
133
|
if (history[0]?.role === "system")
|
|
134
134
|
kept.push(history[0]);
|
|
@@ -138,10 +138,13 @@ export function dropOldestTurns(history, keepTurns) {
|
|
|
138
138
|
kept.push(...history.slice(cut));
|
|
139
139
|
const removed = history.length - kept.length;
|
|
140
140
|
if (removed <= 0)
|
|
141
|
-
return 0;
|
|
141
|
+
return { removed: 0, turns: 0 };
|
|
142
142
|
history.length = 0;
|
|
143
143
|
history.push(...kept);
|
|
144
|
-
|
|
144
|
+
// T-7 · The count of *turns*, not of messages. Reporting a fixed 1 made the
|
|
145
|
+
// display say "dropped 1 earlier turn" after removing eight of them.
|
|
146
|
+
const turns = userAt.filter((at) => at < cut && at !== userAt[0]).length;
|
|
147
|
+
return { removed, turns };
|
|
145
148
|
}
|
|
146
149
|
/**
|
|
147
150
|
* Shorten the history as much as can be done safely.
|
|
@@ -156,12 +159,12 @@ export function dropOldestTurns(history, keepTurns) {
|
|
|
156
159
|
*/
|
|
157
160
|
export function compactForBudget(history, keepRecent = KEEP_RECENT_EXCHANGES) {
|
|
158
161
|
const pruned = pruneToolExchanges(history, keepRecent);
|
|
159
|
-
const
|
|
162
|
+
const dropped = dropOldestTurns(history, keepRecent);
|
|
160
163
|
return {
|
|
161
|
-
removed: pruned.removed +
|
|
164
|
+
removed: pruned.removed + dropped.removed,
|
|
162
165
|
exchanges: pruned.exchanges,
|
|
163
|
-
turns:
|
|
164
|
-
exhausted: pruned.exchanges === 0 &&
|
|
166
|
+
turns: dropped.turns,
|
|
167
|
+
exhausted: pruned.exchanges === 0 && dropped.removed === 0,
|
|
165
168
|
};
|
|
166
169
|
}
|
|
167
170
|
/**
|
|
@@ -180,11 +183,17 @@ export function compactForBudget(history, keepRecent = KEEP_RECENT_EXCHANGES) {
|
|
|
180
183
|
*/
|
|
181
184
|
export function compactUntilItFits(history, contextLength, lastPromptTokens, keepRecent = KEEP_RECENT_EXCHANGES) {
|
|
182
185
|
const startedWith = history.length;
|
|
183
|
-
const
|
|
186
|
+
const floor = fixedOverhead(history, lastPromptTokens);
|
|
187
|
+
const perMessage = startedWith > 0 ? Math.max(0, lastPromptTokens - floor) / startedWith : 0;
|
|
184
188
|
let exchanges = 0;
|
|
185
189
|
let turns = 0;
|
|
186
190
|
for (;;) {
|
|
187
|
-
|
|
191
|
+
// T-2 · The floor does not shrink. The system prompt and the tool schemas
|
|
192
|
+
// are re-sent every turn whatever the history looks like, so treating the
|
|
193
|
+
// whole measurement as proportional to the message count said a
|
|
194
|
+
// conversation had come inside the window when only the removable part
|
|
195
|
+
// had. Everything below the floor is not ours to drop.
|
|
196
|
+
const estimate = floor + Math.round(perMessage * history.length);
|
|
188
197
|
if (!shouldCompact(contextLength, estimate)) {
|
|
189
198
|
return {
|
|
190
199
|
removed: startedWith - history.length,
|
|
@@ -207,3 +216,29 @@ export function compactUntilItFits(history, contextLength, lastPromptTokens, kee
|
|
|
207
216
|
turns += step.turns;
|
|
208
217
|
}
|
|
209
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* T-2 · What the next request costs before any history is added.
|
|
221
|
+
*
|
|
222
|
+
* The system prompt is measurable; the tool schemas are not from here - the
|
|
223
|
+
* loop knows them but this module does not, and CLI-000B found the gateway's
|
|
224
|
+
* own reservation undercounts badly for exactly that reason. So it is
|
|
225
|
+
* approximated from the one message we can size, with a floor that keeps the
|
|
226
|
+
* estimate conservative: over-estimating the fixed part makes compaction try
|
|
227
|
+
* harder, which is the direction that fails safe.
|
|
228
|
+
*/
|
|
229
|
+
function fixedOverhead(history, lastPromptTokens) {
|
|
230
|
+
const system = history.find((m) => m.role === "system");
|
|
231
|
+
if (system === undefined || lastPromptTokens <= 0)
|
|
232
|
+
return 0;
|
|
233
|
+
const chars = (system.content ?? "").length;
|
|
234
|
+
// Roughly four characters per token, doubled to stand in for the tool
|
|
235
|
+
// schemas that are sent alongside it and cannot be measured here.
|
|
236
|
+
const approx = Math.round((chars / 4) * 2);
|
|
237
|
+
// Bounded by the measurement, because the fixed part cannot exceed the whole
|
|
238
|
+
// request - but NOT halved. Capping it lower was the unsafe direction: it
|
|
239
|
+
// let the loop conclude that dropping a few messages had brought a history
|
|
240
|
+
// inside a window the system prompt alone did not fit in. Over-estimating
|
|
241
|
+
// makes compaction try harder and, at worst, stop with an honest message;
|
|
242
|
+
// under-estimating sends a request that fails upstream and is billed.
|
|
243
|
+
return Math.min(approx, lastPromptTokens);
|
|
244
|
+
}
|
package/dist/agent/loop.d.ts
CHANGED
|
@@ -36,6 +36,13 @@ export interface AgentReport {
|
|
|
36
36
|
promptTokens: number;
|
|
37
37
|
completionTokens: number;
|
|
38
38
|
costMicroUsd: bigint;
|
|
39
|
+
/**
|
|
40
|
+
* T-4 · Requests the cost above does not include, because the catalogue had
|
|
41
|
+
* no price for them. Without it every live summary shows `$0.00` for a run
|
|
42
|
+
* that was not free - the session store learned to say "unknown" and the
|
|
43
|
+
* surfaces the user actually watches did not.
|
|
44
|
+
*/
|
|
45
|
+
unpricedRequests: number;
|
|
39
46
|
/** Set when the run ended because something failed. */
|
|
40
47
|
error?: string;
|
|
41
48
|
}
|
package/dist/agent/loop.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAE1B,iBAAiB;AACjB,eAAO,MAAM,cAAc;;;IAGzB,iFAAiF;;;qBAGnD,MAAM,GAAG,SAAS;CACjD,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,OAAO,cAAc,CAAC;AAEhD,MAAM,MAAM,UAAU,GAClB,WAAW,GACX,WAAW,GACX,SAAS,GACT,UAAU,GACV,WAAW,GACX,cAAc;AAChB,8EAA8E;GAC5E,eAAe;AACjB;;;;GAIG;GACD,cAAc,GACd,OAAO,CAAC;AAEZ,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,UAAU,CAAC;IACvB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAC1C,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB;;;OAGG;IACH,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,kEAAkE;IAClE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,uDAAuD;IACvD,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,wBAAwB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AAChC;;;;;GAKG;GACD;IACE,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC;CACjB,GACD;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AAC3D;;;;GAIG;GACD;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEN,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IAC/C,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAYD;;;;;;;;;GASG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"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"}
|
package/dist/agent/loop.js
CHANGED
|
@@ -44,11 +44,22 @@ export async function runAgent(options) {
|
|
|
44
44
|
promptTokens: 0,
|
|
45
45
|
completionTokens: 0,
|
|
46
46
|
costMicroUsd: 0n,
|
|
47
|
+
unpricedRequests: 0,
|
|
47
48
|
};
|
|
48
49
|
/** What the provider counted for the previous request; 0 until one lands. */
|
|
49
50
|
let lastPromptTokens = 0;
|
|
50
51
|
/** R-5 · Turns whose cost the catalogue could not tell us. */
|
|
51
52
|
let unpricedTurns = 0;
|
|
53
|
+
// T-3 · Known before anything is sent. Waiting for the first response to
|
|
54
|
+
// discover it meant a run with an explicit ceiling still made one billable
|
|
55
|
+
// request that could never be counted against it.
|
|
56
|
+
if (limits.maxCostMicroUsd !== undefined &&
|
|
57
|
+
(!options.pricing?.inputPricePer1mMicroUsd ||
|
|
58
|
+
!options.pricing.outputPricePer1mMicroUsd)) {
|
|
59
|
+
return stop(report, "cost_unknown", `--max-cost-usd cannot be enforced: the catalogue publishes no price for ` +
|
|
60
|
+
`${options.model}, so nothing can be counted towards the ceiling. ` +
|
|
61
|
+
`Re-run without the limit to continue anyway.`);
|
|
62
|
+
}
|
|
52
63
|
for (let turn = 1; turn <= limits.maxTurns; turn += 1) {
|
|
53
64
|
if (options.signal?.aborted)
|
|
54
65
|
return stop(report, "cancelled");
|
|
@@ -133,8 +144,10 @@ export async function runAgent(options) {
|
|
|
133
144
|
costMicroUsd: turnCost,
|
|
134
145
|
priced: canPrice(options.pricing, usage),
|
|
135
146
|
});
|
|
136
|
-
if (!canPrice(options.pricing, usage))
|
|
147
|
+
if (!canPrice(options.pricing, usage)) {
|
|
137
148
|
unpricedTurns += 1;
|
|
149
|
+
report.unpricedRequests += 1;
|
|
150
|
+
}
|
|
138
151
|
const choice = response.choices?.[0];
|
|
139
152
|
const message = choice?.message;
|
|
140
153
|
const calls = message?.tool_calls ?? [];
|
package/dist/agent/session.d.ts
CHANGED
|
@@ -32,6 +32,16 @@ export interface SessionOptions {
|
|
|
32
32
|
* the same events are drawn.
|
|
33
33
|
*/
|
|
34
34
|
onEvent?: (event: AgentEvent) => void;
|
|
35
|
+
/**
|
|
36
|
+
* T-1 · The user's own words, which never appear in the agent event stream.
|
|
37
|
+
*
|
|
38
|
+
* Called from the loop rather than from each surface's `readLine`, because
|
|
39
|
+
* that is exactly how it came to be missing: the TUI wrapped its reader and
|
|
40
|
+
* `mafdet agent` did not, so a command-mode session recorded the model's
|
|
41
|
+
* replies and none of the questions - and resuming it could not continue the
|
|
42
|
+
* conversation, because half of it was not there.
|
|
43
|
+
*/
|
|
44
|
+
onGoal?: (goal: string) => void;
|
|
35
45
|
/** R-4 · False when stdout is a pipe or a file, which get exact bytes. */
|
|
36
46
|
isTty?: boolean;
|
|
37
47
|
/**
|
|
@@ -48,6 +58,8 @@ export interface SessionSummary {
|
|
|
48
58
|
promptTokens: number;
|
|
49
59
|
completionTokens: number;
|
|
50
60
|
costMicroUsd: bigint;
|
|
61
|
+
/** T-4 · Requests the cost above does not include; see AgentReport. */
|
|
62
|
+
unpricedRequests: number;
|
|
51
63
|
}
|
|
52
64
|
/**
|
|
53
65
|
* The interactive session (plan §4.4).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/agent/session.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAIrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAY,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAapE,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,oEAAoE;IACpE,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7C,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,0EAA0E;IAC1E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/agent/session.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAIrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAY,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAapE,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,oEAAoE;IACpE,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7C,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,0EAA0E;IAC1E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,cAAc,CAAC,CAwGzB;AAsHD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CA2B7D"}
|
package/dist/agent/session.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createInterface } from "node:readline/promises";
|
|
2
2
|
import { isAgentEligible } from "../api/models.js";
|
|
3
|
-
import {
|
|
3
|
+
import { formatUsdWithUnpriced } from "../output/money.js";
|
|
4
4
|
import { renderRecord } from "../output/format.js";
|
|
5
5
|
import { safeForTerminal } from "../output/terminal-safe.js";
|
|
6
6
|
import { runAgent } from "./loop.js";
|
|
@@ -32,6 +32,7 @@ export async function runSession(options) {
|
|
|
32
32
|
promptTokens: 0,
|
|
33
33
|
completionTokens: 0,
|
|
34
34
|
costMicroUsd: 0n,
|
|
35
|
+
unpricedRequests: 0,
|
|
35
36
|
};
|
|
36
37
|
let model = options.model;
|
|
37
38
|
let pricing = options.pricing;
|
|
@@ -69,6 +70,7 @@ export async function runSession(options) {
|
|
|
69
70
|
break;
|
|
70
71
|
continue;
|
|
71
72
|
}
|
|
73
|
+
options.onGoal?.(line);
|
|
72
74
|
summary.goals += 1;
|
|
73
75
|
const report = await runAgent({
|
|
74
76
|
client: options.client,
|
|
@@ -95,6 +97,7 @@ export async function runSession(options) {
|
|
|
95
97
|
summary.promptTokens += report.promptTokens;
|
|
96
98
|
summary.completionTokens += report.completionTokens;
|
|
97
99
|
summary.costMicroUsd += report.costMicroUsd;
|
|
100
|
+
summary.unpricedRequests += report.unpricedRequests;
|
|
98
101
|
// R-4 · The model's answer reaches a terminal here too. The TUI wraps
|
|
99
102
|
// this writer, but `mafdet agent` without a goal does not, so the
|
|
100
103
|
// sanitising belongs at the sink rather than at one of its callers.
|
|
@@ -180,7 +183,10 @@ async function handleCommand(line, context) {
|
|
|
180
183
|
"tokens",
|
|
181
184
|
`${context.summary.promptTokens} in / ${context.summary.completionTokens} out`,
|
|
182
185
|
],
|
|
183
|
-
[
|
|
186
|
+
[
|
|
187
|
+
"cost",
|
|
188
|
+
`~${formatUsdWithUnpriced(context.summary.costMicroUsd, context.summary.unpricedRequests)}`,
|
|
189
|
+
],
|
|
184
190
|
]));
|
|
185
191
|
return false;
|
|
186
192
|
case "/compact": {
|
package/dist/api/models.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/api/models.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/api/models.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,0CAA0C;AAC1C,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,aAAc,SAAQ,YAAY;IACjD,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,mDAAmD;IACnD,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,sFAAsF;IACtF,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gDAAgD;IAChD,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,CAQxE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,SAAS,cAAc,EAAE,EACpC,OAAO,EAAE,SAAS,YAAY,EAAE,GAC/B,SAAS,CAuBX;AAED,8CAA8C;AAC9C,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,cAAc,EAAE,CAAC,CAQ3B;AAED,2EAA2E;AAC3E,wBAAsB,YAAY,CAChC,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAGzB;AA0BD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,YAAY,GAAG,SAAS,CAY1B;AAED,qBAAa,oBAAqB,SAAQ,KAAK;IAE3C,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM;gBAFhB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM;CAO5B"}
|
package/dist/api/models.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { stripAnsi } from "../agent/command-policy.js";
|
|
1
2
|
/**
|
|
2
3
|
* Agent admission. Every condition is something the catalogue states outright;
|
|
3
4
|
* nothing is inferred from the model id, the provider or the display name —
|
|
@@ -51,12 +52,37 @@ export function intersectModels(dataPlane, catalog) {
|
|
|
51
52
|
/** GET /v1/models — the key's access list. */
|
|
52
53
|
export async function fetchAccessibleModels(client) {
|
|
53
54
|
const { data } = await client.getJson("/models");
|
|
54
|
-
|
|
55
|
+
// T-5 · Same reason as `cleanModel`: these ids are printed as the "unlisted"
|
|
56
|
+
// set, so they reach a terminal without ever passing through the catalogue.
|
|
57
|
+
return (data.data ?? []).map((model) => ({
|
|
58
|
+
...model,
|
|
59
|
+
id: typeof model.id === "string" ? stripAnsi(model.id) : model.id,
|
|
60
|
+
}));
|
|
55
61
|
}
|
|
56
62
|
/** GET /models on the business API — public catalogue, no key required. */
|
|
57
63
|
export async function fetchCatalog(client) {
|
|
58
64
|
const { data } = await client.getJson("/models");
|
|
59
|
-
return data.data ?? [];
|
|
65
|
+
return (data.data ?? []).map(cleanModel);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* T-5 · Strip terminal control sequences from catalogue text, once, here.
|
|
69
|
+
*
|
|
70
|
+
* The catalogue is remote data that ends up on a terminal in the picker, in
|
|
71
|
+
* `models list`, in `models show`, in `doctor` and in error messages. F-010
|
|
72
|
+
* was fixed sink by sink across two review rounds and the picker was still
|
|
73
|
+
* missing, which is the argument for doing it where the data enters instead:
|
|
74
|
+
* every renderer downstream, present and future, then has nothing to forget.
|
|
75
|
+
*
|
|
76
|
+
* The model id is cleaned too. It is sent back to the API, so in principle
|
|
77
|
+
* this changes what we would ask for - but an id carrying a control character
|
|
78
|
+
* is not a model id, and declining to echo one back is right either way.
|
|
79
|
+
*/
|
|
80
|
+
function cleanModel(model) {
|
|
81
|
+
const clean = (value) => typeof value === "string" ? stripAnsi(value) : value;
|
|
82
|
+
return Object.fromEntries(Object.entries(model).map(([key, value]) => [
|
|
83
|
+
key,
|
|
84
|
+
Array.isArray(value) ? value.map(clean) : clean(value),
|
|
85
|
+
]));
|
|
60
86
|
}
|
|
61
87
|
/**
|
|
62
88
|
* Check the model serves the endpoint the command needs, before spending a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse-parser.d.ts","sourceRoot":"","sources":["../../src/api/sse-parser.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;CACf;AAWD,+EAA+E;AAC/E,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAE7C,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,EAAE;IAMjC,kDAAkD;IAClD,GAAG,IAAI,MAAM,EAAE;IASf,OAAO,CAAC,KAAK;CAYd;AAED,8DAA8D;AAC9D,qBAAa,mBAAmB;IAOlB,OAAO,CAAC,QAAQ,CAAC,MAAM;IANnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuC;IAC7D,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,KAAK,CAAC,CAAc;IAC5B,OAAO,CAAC,IAAI,CAAS;gBAEQ,MAAM,GAAE,YAAiB;IAEtD,4DAA4D;IAC5D,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAgD9B,MAAM,IAAI,YAAY;CASvB;AAED,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,eAAO,MAAM,sBAAsB,SAAU,CAAC;AAC9C,eAAO,MAAM,wBAAwB,QAAmB,CAAC;AACzD,0EAA0E;AAC1E,eAAO,MAAM,uBAAuB,QAAc,CAAC;AAEnD;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,QAAQ,EAClB,MAAM,GAAE,YAAiB,EACzB,MAAM,CAAC,EAAE,WAAW,EACpB,MAAM,GAAE,YAAiB,GACxB,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"sse-parser.d.ts","sourceRoot":"","sources":["../../src/api/sse-parser.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;CACf;AAWD,+EAA+E;AAC/E,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAE7C,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,EAAE;IAMjC,kDAAkD;IAClD,GAAG,IAAI,MAAM,EAAE;IASf,OAAO,CAAC,KAAK;CAYd;AAED,8DAA8D;AAC9D,qBAAa,mBAAmB;IAOlB,OAAO,CAAC,QAAQ,CAAC,MAAM;IANnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuC;IAC7D,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,KAAK,CAAC,CAAc;IAC5B,OAAO,CAAC,IAAI,CAAS;gBAEQ,MAAM,GAAE,YAAiB;IAEtD,4DAA4D;IAC5D,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAgD9B,MAAM,IAAI,YAAY;CASvB;AAED,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,eAAO,MAAM,sBAAsB,SAAU,CAAC;AAC9C,eAAO,MAAM,wBAAwB,QAAmB,CAAC;AACzD,0EAA0E;AAC1E,eAAO,MAAM,uBAAuB,QAAc,CAAC;AAEnD;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,QAAQ,EAClB,MAAM,GAAE,YAAiB,EACzB,MAAM,CAAC,EAAE,WAAW,EACpB,MAAM,GAAE,YAAiB,GACxB,OAAO,CAAC,YAAY,CAAC,CAqGvB"}
|
package/dist/api/sse-parser.js
CHANGED
|
@@ -153,14 +153,23 @@ export async function readChatStream(response, events = {}, signal, limits = {})
|
|
|
153
153
|
message: `the stream ran for longer than ${totalMs}ms`,
|
|
154
154
|
});
|
|
155
155
|
}
|
|
156
|
-
|
|
156
|
+
// T-6 · Whichever runs out first. Checking the total before the read
|
|
157
|
+
// and then waiting a whole idle period overshot the total by up to one
|
|
158
|
+
// of them - with a 30 minute total and a 2 minute idle that is a
|
|
159
|
+
// rounding error, but with the total below the idle it never applied
|
|
160
|
+
// at all.
|
|
161
|
+
const remaining = totalMs - (Date.now() - startedAt);
|
|
162
|
+
const chunk = await withIdleDeadline(reader.read(), Math.max(0, Math.min(idleMs, remaining)));
|
|
157
163
|
if (chunk === IDLE) {
|
|
158
164
|
limits.abort?.();
|
|
159
165
|
await reader.cancel().catch(() => { });
|
|
166
|
+
const overTotal = Date.now() - startedAt >= totalMs;
|
|
160
167
|
throw new ApiError({
|
|
161
168
|
kind: "timeout",
|
|
162
169
|
exitCode: ExitCode.Unavailable,
|
|
163
|
-
message:
|
|
170
|
+
message: overTotal
|
|
171
|
+
? `the stream ran for longer than ${totalMs}ms`
|
|
172
|
+
: `the stream sent nothing for ${idleMs}ms`,
|
|
164
173
|
});
|
|
165
174
|
}
|
|
166
175
|
const { done, value } = chunk;
|
package/dist/commands/agent.d.ts
CHANGED
|
@@ -8,6 +8,14 @@ export interface AgentIo extends CommandIo {
|
|
|
8
8
|
/** Overridden in tests; production reads the TTY. */
|
|
9
9
|
isInteractive?: boolean;
|
|
10
10
|
approver?: import("../agent/approvals.js").Approver;
|
|
11
|
+
/**
|
|
12
|
+
* Overridden in tests; production reads lines from the terminal.
|
|
13
|
+
*
|
|
14
|
+
* Injectable because the interactive path could otherwise only be exercised
|
|
15
|
+
* under a real pty - and `script` cannot allocate one in every CI, so the
|
|
16
|
+
* wiring here went untested and a broken edit shipped past a full suite.
|
|
17
|
+
*/
|
|
18
|
+
readLine?: () => Promise<string | undefined>;
|
|
11
19
|
}
|
|
12
20
|
export declare function agentCommand(argv: readonly string[], io: AgentIo): Promise<ExitCode>;
|
|
13
21
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAQA,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAiBvE,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAGnD,OAAO,EAIL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAmBtB,eAAO,MAAM,gBAAgB,0XAK+C,CAAC;AAE7E,eAAO,MAAM,YAAY,mgBAOmB,CAAC;AAE7C,MAAM,WAAW,OAAQ,SAAQ,SAAS;IACxC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,qDAAqD;IACrD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,uBAAuB,EAAE,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAQA,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAiBvE,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAGnD,OAAO,EAIL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAmBtB,eAAO,MAAM,gBAAgB,0XAK+C,CAAC;AAE7E,eAAO,MAAM,YAAY,mgBAOmB,CAAC;AAE7C,MAAM,WAAW,OAAQ,SAAQ,SAAS;IACxC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,qDAAqD;IACrD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,uBAAuB,EAAE,QAAQ,CAAC;IACpD;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC9C;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,EAAE,OAAO,GACV,OAAO,CAAC,QAAQ,CAAC,CAuInB;AAoLD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,EAAE,kBAAkB,EAAE,OAAO,0JAI3E"}
|
package/dist/commands/agent.js
CHANGED
|
@@ -16,7 +16,7 @@ import { WRITE_TOOLS } from "../agent/tools/write.js";
|
|
|
16
16
|
import { Workspace, WorkspaceError } from "../filesystem/workspace.js";
|
|
17
17
|
import { ExitCode } from "../output/exit-codes.js";
|
|
18
18
|
import { renderJson, renderRecord } from "../output/format.js";
|
|
19
|
-
import {
|
|
19
|
+
import { formatUsdWithUnpriced } from "../output/money.js";
|
|
20
20
|
import { buildRuntime, reportError, requireApiKey, } from "./runtime.js";
|
|
21
21
|
const USAGE = `Usage:
|
|
22
22
|
mafdet agent "<goal>" [--model <id>] [--allow-write] [--read-only]
|
|
@@ -99,7 +99,13 @@ export async function agentCommand(argv, io) {
|
|
|
99
99
|
client: runtime.dataPlane,
|
|
100
100
|
...(recorder === undefined
|
|
101
101
|
? {}
|
|
102
|
-
: {
|
|
102
|
+
: {
|
|
103
|
+
onEvent: (event) => recorder.observe(event),
|
|
104
|
+
// T-1 · The user's questions. Without this the transcript held
|
|
105
|
+
// the replies and none of the prompts, so resuming it could
|
|
106
|
+
// not continue a conversation that was only half there.
|
|
107
|
+
onGoal: (goal) => recorder.goal(goal),
|
|
108
|
+
}),
|
|
103
109
|
model,
|
|
104
110
|
pricing: resolved,
|
|
105
111
|
catalog,
|
|
@@ -113,6 +119,7 @@ export async function agentCommand(argv, io) {
|
|
|
113
119
|
stderr: io.stderr,
|
|
114
120
|
signal: io.signal,
|
|
115
121
|
isTty: isTty(io),
|
|
122
|
+
...(io.readLine === undefined ? {} : { readLine: io.readLine }),
|
|
116
123
|
});
|
|
117
124
|
}
|
|
118
125
|
finally {
|
|
@@ -127,7 +134,10 @@ export async function agentCommand(argv, io) {
|
|
|
127
134
|
"tokens",
|
|
128
135
|
`${summary.promptTokens} in / ${summary.completionTokens} out`,
|
|
129
136
|
],
|
|
130
|
-
[
|
|
137
|
+
[
|
|
138
|
+
"cost",
|
|
139
|
+
`~${formatUsdWithUnpriced(summary.costMicroUsd, summary.unpricedRequests)}`,
|
|
140
|
+
],
|
|
131
141
|
]));
|
|
132
142
|
return ExitCode.Ok;
|
|
133
143
|
}
|
|
@@ -344,7 +354,10 @@ function reportEvent(event, io) {
|
|
|
344
354
|
/** Exit codes follow §4.5: a ceiling is 7, a cancel is 6. */
|
|
345
355
|
function finish(report, json, io) {
|
|
346
356
|
if (json) {
|
|
347
|
-
io.stdout(
|
|
357
|
+
io.stdout(
|
|
358
|
+
// T-4 · `unpricedRequests` rides along in the report, so a consumer can
|
|
359
|
+
// tell a genuinely free run from one nobody could price.
|
|
360
|
+
renderJson({ ...report, costMicroUsd: report.costMicroUsd.toString() }));
|
|
348
361
|
}
|
|
349
362
|
else {
|
|
350
363
|
// R-4 · The model's answer, sanitised only when a terminal is reading it.
|
|
@@ -365,7 +378,10 @@ function finish(report, json, io) {
|
|
|
365
378
|
"tokens",
|
|
366
379
|
`${report.promptTokens} in / ${report.completionTokens} out`,
|
|
367
380
|
],
|
|
368
|
-
[
|
|
381
|
+
[
|
|
382
|
+
"cost",
|
|
383
|
+
`~${formatUsdWithUnpriced(report.costMicroUsd, report.unpricedRequests)}`,
|
|
384
|
+
],
|
|
369
385
|
]));
|
|
370
386
|
if (report.error)
|
|
371
387
|
io.stderr(`error: ${report.error}\n`);
|
|
@@ -351,11 +351,16 @@ async function usage(argv, io, root) {
|
|
|
351
351
|
prompt_tokens: m.promptTokens,
|
|
352
352
|
completion_tokens: m.completionTokens,
|
|
353
353
|
estimated_charge_micro_usd: m.estimatedMicroUsd.toString(),
|
|
354
|
+
// T-10 · Without this a caller cannot tell whether one model's "0"
|
|
355
|
+
// is free or unpriced, even though the top-level total says some
|
|
356
|
+
// requests were not counted.
|
|
357
|
+
unpriced_requests: m.unpricedRequests,
|
|
354
358
|
})),
|
|
355
359
|
by_api_key: totals.byKey.map((k) => ({
|
|
356
360
|
api_key_ref: k.apiKeyRef,
|
|
357
361
|
model_requests: k.modelRequests,
|
|
358
362
|
estimated_charge_micro_usd: k.estimatedMicroUsd.toString(),
|
|
363
|
+
unpriced_requests: k.unpricedRequests,
|
|
359
364
|
})),
|
|
360
365
|
...(refreshed === undefined ? {} : { refresh: jsonRefresh(refreshed) }),
|
|
361
366
|
}));
|
|
@@ -396,7 +401,9 @@ async function usage(argv, io, root) {
|
|
|
396
401
|
{ header: "REQUESTS", value: (m) => `${m.modelRequests}` },
|
|
397
402
|
{
|
|
398
403
|
header: "ESTIMATE",
|
|
399
|
-
value: (m) =>
|
|
404
|
+
value: (m) => m.unpricedRequests > 0
|
|
405
|
+
? `${formatUsd(m.estimatedMicroUsd.toString())} + ${m.unpricedRequests} unpriced`
|
|
406
|
+
: formatUsd(m.estimatedMicroUsd.toString()),
|
|
400
407
|
},
|
|
401
408
|
]));
|
|
402
409
|
}
|
|
@@ -408,7 +415,9 @@ async function usage(argv, io, root) {
|
|
|
408
415
|
{ header: "REQUESTS", value: (k) => `${k.modelRequests}` },
|
|
409
416
|
{
|
|
410
417
|
header: "ESTIMATE",
|
|
411
|
-
value: (k) =>
|
|
418
|
+
value: (k) => k.unpricedRequests > 0
|
|
419
|
+
? `${formatUsd(k.estimatedMicroUsd.toString())} + ${k.unpricedRequests} unpriced`
|
|
420
|
+
: formatUsd(k.estimatedMicroUsd.toString()),
|
|
412
421
|
},
|
|
413
422
|
]));
|
|
414
423
|
}
|
package/dist/output/money.d.ts
CHANGED
|
@@ -13,4 +13,13 @@ export declare function parseMicroUsd(value: string | number | bigint): bigint;
|
|
|
13
13
|
export declare function formatUsd(microUsd: string | number | bigint, options?: {
|
|
14
14
|
minFractionDigits?: number;
|
|
15
15
|
}): string;
|
|
16
|
+
/**
|
|
17
|
+
* T-4 · A cost that may be incomplete, described rather than rounded to zero.
|
|
18
|
+
*
|
|
19
|
+
* Four surfaces render a running total - the command summary, the TUI summary,
|
|
20
|
+
* `/cost` and the JSON report - and each one showed `$0.00` for a run whose
|
|
21
|
+
* model the catalogue could not price. The session store had already learned
|
|
22
|
+
* to say "unknown"; the places the user actually watches had not.
|
|
23
|
+
*/
|
|
24
|
+
export declare function formatUsdWithUnpriced(microUsd: bigint | string, unpricedRequests: number): string;
|
|
16
25
|
//# sourceMappingURL=money.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../../src/output/money.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAOrE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAClC,OAAO,GAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC3C,MAAM,CAeR"}
|
|
1
|
+
{"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../../src/output/money.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAOrE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAClC,OAAO,GAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC3C,MAAM,CAeR;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,GAAG,MAAM,EACzB,gBAAgB,EAAE,MAAM,GACvB,MAAM,CAIR"}
|
package/dist/output/money.js
CHANGED
|
@@ -33,3 +33,17 @@ export function formatUsd(microUsd, options = {}) {
|
|
|
33
33
|
digits = fraction.slice(0, minDigits);
|
|
34
34
|
return `${negative ? "-" : ""}$${whole}.${digits}`;
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* T-4 · A cost that may be incomplete, described rather than rounded to zero.
|
|
38
|
+
*
|
|
39
|
+
* Four surfaces render a running total - the command summary, the TUI summary,
|
|
40
|
+
* `/cost` and the JSON report - and each one showed `$0.00` for a run whose
|
|
41
|
+
* model the catalogue could not price. The session store had already learned
|
|
42
|
+
* to say "unknown"; the places the user actually watches had not.
|
|
43
|
+
*/
|
|
44
|
+
export function formatUsdWithUnpriced(microUsd, unpricedRequests) {
|
|
45
|
+
const shown = formatUsd(typeof microUsd === "bigint" ? microUsd : microUsd);
|
|
46
|
+
if (unpricedRequests <= 0)
|
|
47
|
+
return shown;
|
|
48
|
+
return `${shown} + ${unpricedRequests} unpriced`;
|
|
49
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-surface.d.ts","sourceRoot":"","sources":["../../src/tui/agent-surface.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,UAAU,EACV,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAW,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGhE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAClD,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,IAAI,CACpB,cAAc,EACd,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,CAC1D,CAAC;IACF;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,WAAW,GAAG,SAAS,CAAC;IACnD,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC;IACpC,yDAAyD;IACzD,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,UAAU,CAAC;CAClC;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,EAAE,EAAE,QAAQ,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA+B7B;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,cAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"agent-surface.d.ts","sourceRoot":"","sources":["../../src/tui/agent-surface.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,UAAU,EACV,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAW,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGhE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAClD,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,IAAI,CACpB,cAAc,EACd,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,CAC1D,CAAC;IACF;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,WAAW,GAAG,SAAS,CAAC;IACnD,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC;IACpC,yDAAyD;IACzD,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,UAAU,CAAC;CAClC;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,EAAE,EAAE,QAAQ,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA+B7B;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,cAAc,CAAC,CA+EzB;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,oBAAoB,GACjC,MAAM,CAqBR"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// different models, tools, approvals or costs, because there is only one
|
|
7
7
|
// implementation of all four. What differs is the drawing.
|
|
8
8
|
import { runSession, } from "../agent/session.js";
|
|
9
|
-
import {
|
|
9
|
+
import { formatUsdWithUnpriced } from "../output/money.js";
|
|
10
10
|
import { tuiApprover } from "./approval.js";
|
|
11
11
|
import { palette } from "./theme.js";
|
|
12
12
|
import { renderCard, ToolCardLog } from "./tool-card.js";
|
|
@@ -99,16 +99,13 @@ export async function runAgentSurface(options) {
|
|
|
99
99
|
// Checkpoint between goals rather than per event: the listing only needs
|
|
100
100
|
// to be current when someone might go and look at it.
|
|
101
101
|
options.recorder?.checkpoint();
|
|
102
|
-
|
|
103
|
-
// The user's own words never appear in the agent event stream, so they
|
|
104
|
-
// are recorded here. Slash commands are control, not conversation.
|
|
105
|
-
if (line !== undefined &&
|
|
106
|
-
line.trim().length > 0 &&
|
|
107
|
-
!line.startsWith("/")) {
|
|
108
|
-
options.recorder?.goal(line);
|
|
109
|
-
}
|
|
110
|
-
return line;
|
|
102
|
+
return promptLine(io, "> ");
|
|
111
103
|
},
|
|
104
|
+
// T-1 · Recording the goal belongs to the session loop now, so both
|
|
105
|
+
// surfaces get it from the same place instead of one remembering.
|
|
106
|
+
...(options.recorder === undefined
|
|
107
|
+
? {}
|
|
108
|
+
: { onGoal: (goal) => options.recorder?.goal(goal) }),
|
|
112
109
|
onEvent,
|
|
113
110
|
};
|
|
114
111
|
// Installed after the spread, on purpose - see getSignal above.
|
|
@@ -133,7 +130,7 @@ export function renderSummary(summary, capabilities) {
|
|
|
133
130
|
dim(` model requests ${summary.modelRequests}`),
|
|
134
131
|
dim(` tool runs ${summary.toolRuns}`),
|
|
135
132
|
dim(` tokens ${summary.promptTokens} in / ${summary.completionTokens} out`),
|
|
136
|
-
dim(` estimated cost ${
|
|
133
|
+
dim(` estimated cost ${formatUsdWithUnpriced(summary.costMicroUsd, summary.unpricedRequests)}`),
|
|
137
134
|
"",
|
|
138
135
|
].join("\n") + "\n");
|
|
139
136
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-centre.d.ts","sourceRoot":"","sources":["../../src/tui/session-centre.ts"],"names":[],"mappings":"AAQA,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAW,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAKrD,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;CAAE,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC;AAEnC,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,UAAU,EAAE,CAQlE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,UAAU,EACjB,YAAY,EAAE,oBAAoB,GACjC,MAAM,
|
|
1
|
+
{"version":3,"file":"session-centre.d.ts","sourceRoot":"","sources":["../../src/tui/session-centre.ts"],"names":[],"mappings":"AAQA,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAW,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAKrD,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;CAAE,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC;AAEnC,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,UAAU,EAAE,CAQlE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,UAAU,EACjB,YAAY,EAAE,oBAAoB,GACjC,MAAM,CAYR;AAOD,wBAAsB,aAAa,CACjC,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,aAAa,CAAC,CAoBxB"}
|
|
@@ -30,7 +30,11 @@ export function describeSession(entry, capabilities) {
|
|
|
30
30
|
stripAnsi(entry.model_id),
|
|
31
31
|
stripAnsi(workspaceName(entry.workspace)),
|
|
32
32
|
`${entry.model_requests} request(s)`,
|
|
33
|
-
|
|
33
|
+
// T-8 · `sessions list/show/usage` learned to say "unknown"; this list
|
|
34
|
+
// did not, so the same session read as free in the picker.
|
|
35
|
+
entry.unpriced_requests
|
|
36
|
+
? `~${formatUsd(entry.estimated_charge_micro_usd)} + ${entry.unpriced_requests} unpriced`
|
|
37
|
+
: `~${formatUsd(entry.estimated_charge_micro_usd)}`,
|
|
34
38
|
];
|
|
35
39
|
return palette(capabilities).dim(parts.join(" - "));
|
|
36
40
|
}
|