@nightlab-agent/subagent-spend 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,8 +7,8 @@
7
7
  {
8
8
  "name": "subagent-spend",
9
9
  "source": "./",
10
- "description": "Per-subagent tokens, rate-limit windows, context fill, cost, steps, tool calls and tool errors of a Claude Code session, in a notice under the prompt, local files and panes (the usage pane not yet seen on a screen).",
11
- "version": "0.7.2",
10
+ "description": "A spend cap for interactive sessions, and per-subagent tokens, rate-limit windows, context fill, cost, steps, tool calls and tool errors of a Claude Code session, in a notice under the prompt, local files and panes (the usage pane not yet seen on a screen).",
11
+ "version": "0.8.0",
12
12
  "category": "productivity"
13
13
  }
14
14
  ],
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "subagent-spend",
3
- "version": "0.7.2",
4
- "description": "Per-subagent tokens, rate-limit windows, context fill, cost, steps, tool calls and tool errors of a Claude Code session, in a notice under the prompt, local files and panes (the usage pane not yet seen on a screen).",
3
+ "version": "0.8.0",
4
+ "description": "A spend cap for interactive sessions, and per-subagent tokens, rate-limit windows, context fill, cost, steps, tool calls and tool errors of a Claude Code session, in a notice under the prompt, local files and panes (the usage pane not yet seen on a screen).",
5
5
  "author": {
6
6
  "name": "Nightlab"
7
7
  },
@@ -20,7 +20,8 @@
20
20
  "usage",
21
21
  "headless",
22
22
  "max-turns",
23
- "budget"
23
+ "budget",
24
+ "spend-cap"
24
25
  ],
25
26
  "userConfig": {
26
27
  "dir": {
@@ -52,6 +53,12 @@
52
53
  "title": "Turn counts folder",
53
54
  "description": "Folder for the day's counts file. A relative path is under the project the session runs in; give an absolute path to keep every project in one place.",
54
55
  "default": ".turn-tally"
56
+ },
57
+ "capUsd": {
58
+ "type": "number",
59
+ "title": "Spend cap (dollars)",
60
+ "description": "Stop sending requests once the session reaches this cost, by Claude Code's own estimate: at 80 percent a notice, at the cap the next model step is answered by the mod with no request made, until /spend-cap raises or lifts it. 0 is off.",
61
+ "default": 0
55
62
  }
56
63
  }
57
64
  }
package/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  The form is Keep a Changelog; versions follow SemVer.
4
4
 
5
+ ## 0.8.0 - 2026-11-01
6
+
7
+ ### Added
8
+ - A spend cap for interactive sessions (`capUsd`, off by default). Claude Code's own `--max-budget-usd` works only
9
+ in print mode (`claude -p`). At 80 percent of the cap a notice says so, once. At the cap, the next model step
10
+ (the main conversation's or a subagent's) is answered by the mod and no request goes out, until `/spend-cap`
11
+ raises the cap (`/spend-cap 5`) or lifts it (`/spend-cap off`). A step already running finishes, so its cost can
12
+ pass the cap. The figure is Claude Code's own client-side estimate, which can differ from your bill. Eight tests;
13
+ with the hold taken out, five of them fail. The cost is read before every step, not only after a turn, so a
14
+ tool loop or a subagent inside one turn is held too (two more tests, each beside a comparison case).
15
+ - With the cap on, the status line shows the cost the cap holds on, so it moves inside a turn too. When two
16
+ subagents step at once and a cost reading comes back late, the highest reading in the session is kept: a late,
17
+ lower one cannot set the count back under the cap, and a read still out when a session ends does not land in
18
+ the next one.
19
+
20
+ ## 0.7.3 - 2026-10-31
21
+
22
+ ### Fixed
23
+ - The pane read "1 subagents" and "1 turns" when there was one; it now reads "1 subagent" and "1 turn". Three tests
24
+ had the old wording written into them and now expect the singular; one new test checks one and two of each.
25
+
26
+ ### Changed
27
+ - The README says the pane was seen updating step by step on a screen (a preview recording on 2026-10-31).
28
+
5
29
  ## 0.7.2 - 2026-10-30
6
30
 
7
31
  ### Added
package/README.md CHANGED
@@ -5,7 +5,8 @@ Claude Code meters: subagent tokens, usage limits and turn counts.
5
5
  This mod (a plugin that adds behaviour to Claude Code) shows three things in
6
6
  one line under the prompt and writes each to a small local file a script can
7
7
  read: the tokens each subagent used, summed per subagent; the usage limits;
8
- and a running count of steps, tool calls and tool errors. It also has side
8
+ and a running count of steps, tool calls and tool errors. If you set one, a
9
+ spend cap stops sending requests once an interactive session's cost reaches it. It also has side
9
10
  panes: the subagent pane is seen below; the Usage pane is not yet seen on a
10
11
  real screen. Nothing leaves your machine.
11
12
 
@@ -21,7 +22,7 @@ was cut with "…".
21
22
  ## Install
22
23
 
23
24
  One line at the Claude Code prompt (Claude Code 2.1.275 or later; the tests
24
- run on 2.1.310 and the previews on 2.1.312):
25
+ and previews run on 2.1.312):
25
26
 
26
27
  ```
27
28
  /plugin install subagent-spend --marketplace nightlab-agent/subagent-spend
@@ -116,12 +117,34 @@ preview showed, after one turn with one subagent:
116
117
  ⚠ subagent-spend: subagents 1 | tok 18k (out 1068) | 5h 49% | 7d 26% | ctx 4% | $0.17 | steps 3 | tools 1 | errors …
117
118
  ```
118
119
 
120
+ ## Spend cap
121
+
122
+ Claude Code's own cap, `--max-budget-usd`, works only in print mode
123
+ (`claude -p`, per its [CLI reference](https://code.claude.com/docs/en/cli-reference)).
124
+ For an interactive session, set `capUsd` in `/config` to a number of dollars.
125
+ At 80 percent of it a notice says so, once. At the cap, the next model step,
126
+ the main conversation's or a subagent's, is answered by the mod and no request
127
+ goes out, until you raise it (`/spend-cap 5`) or lift it (`/spend-cap off`)
128
+ for the session. `/spend-cap` alone shows the cap and the cost so far.
129
+
130
+ ![Claude Code with a $0.10 cap: asked to read every file and describe each, Claude reads four files; the next step is answered by the mod's line saying the session cost $0.11 of the $0.10 cap and no request was sent](https://raw.githubusercontent.com/nightlab-agent/subagent-spend/bcb368556c20a40c62ce952574e5a1bec5210923/docs/spend-cap-midturn.png)
131
+
132
+ Seen in a Claude Code 2.1.312 terminal on 2026-10-31 with the cap set to
133
+ $0.10: one prompt asked Claude to read every file and describe each; after
134
+ it had read four files the cost was $0.11, and the next step, inside the
135
+ same turn, got the line above instead of a request. In an earlier run with
136
+ a $0.05 cap, the first prompt cost $0.06 and the second got the same line in
137
+ 0 seconds, with no tool call. The figure is Claude Code's own client-side
138
+ estimate, which can differ from your bill. A step already running finishes,
139
+ so the cost can pass the cap: leave headroom. The 80 percent notice is
140
+ tested, not yet seen on a screen.
141
+
119
142
  ## What each part shows
120
143
 
121
144
  **Subagent spend.** For each subagent: model turns (one request each), and
122
145
  the input, output, cache-read and cache-creation tokens the API reported for
123
- each of its steps, summed as the steps happen (the pane updating step by
124
- step is not yet seen on a real screen). `context` is the last step's
146
+ each of its steps, summed as the steps happen (seen in a terminal preview recording on
147
+ 2026-10-31: three subagents' total climbing from 66k to 209k tokens as they ran). `context` is the last step's
125
148
  prompt (input, cache read and cache creation) plus its reply: how full the
126
149
  subagent's context was when it ended. The pane opens by itself at the first subagent of a session and lists them, biggest spender first, as many as its height holds;
127
150
  `/subagent-spend` opens it again. An agent id is shown by its first six
@@ -162,6 +185,7 @@ Set them in `/config`.
162
185
  | `usageFileDir` | `.usage-file` | Folder for `latest.json`. |
163
186
  | `turnTally` | on | The turn counts. Off: nothing is counted, no counts in the status line, no day file. |
164
187
  | `turnTallyDir` | `.turn-tally` | Folder for the day files. |
188
+ | `capUsd` | 0 (off) | The spend cap in dollars (see Spend cap). |
165
189
 
166
190
  A relative folder is under the project the session runs in; an absolute
167
191
  path keeps every project's files in one place. An option that is off
@@ -269,19 +293,20 @@ environment variable. Everything stays on your machine.
269
293
 
270
294
  | Hook | What it does | What it changes |
271
295
  |---|---|---|
272
- | `session.start` | Registers `/subagent-spend`, and `/usage-file` when the usage part is on. | Nothing. |
273
- | `command.run` | Opens the pane the command names. | Nothing. |
296
+ | `session.start` | Registers `/subagent-spend` and `/spend-cap`, and `/usage-file` when the usage part is on. | Nothing. |
297
+ | `command.run` | Opens the pane the command names; `/spend-cap` shows, sets or lifts the cap for the session. | Nothing. |
274
298
  | `ui.render` (its own panes) | Draws the subagent rows, or the usage bars, from memory. | Nothing. |
275
299
  | `agent.spawn` | Notes the subagent's type; opens the spend pane at the first one. | Nothing: the spawn goes on as asked, even if the mod fails. |
276
300
  | `tool.call` | Counts the call, and whether it came back as an error (turn counts). | Nothing: the call runs as asked and its result passes through. |
277
- | `session.measure` | Updates the usage figures, the status line and `latest.json`; opens the Usage pane once at 80 percent. | Nothing: the event passes through untouched. |
301
+ | `session.measure` | Updates the usage figures, the status line and `latest.json`; opens the Usage pane once at 80 percent; notes the cost for the spend cap and shows its 80 percent notice. | Nothing: the event passes through untouched. |
278
302
  | `session.end` | At a `/clear` or a resume: the subagent rows, the panes' one-time opening and `/subagent-spend` start over for the next session. | Nothing: the event passes through untouched. |
279
- | `turn.step` | Main conversation: counts the step and rewrites the day file. Subagent: adds the step's tokens to its row and rewrites the rows file. | Nothing: the request and the response pass through untouched. |
303
+ | `turn.step` | Main conversation: counts the step and rewrites the day file. Subagent: adds the step's tokens to its row and rewrites the rows file. | Nothing, unless a spend cap you set is reached: then the step is answered with the cap's line and no request goes out. |
280
304
 
281
305
  ## What it does not do
282
306
 
283
307
  No dollars per subagent (plans have no price per token; use the counts).
284
- No cloud or remote sessions. It never denies or rewrites a request, and it
308
+ No cloud or remote sessions. Apart from a spend cap you set, it never denies or
309
+ rewrites a request, and it
285
310
  does not poll: with no event from Claude Code there is no write. It keeps no
286
311
  history beyond the files it leaves, and it does not say which model a usage
287
312
  window is for; Claude Code reports the windows as the account's.
@@ -328,13 +353,14 @@ sh tests/release.sh
328
353
  ```
329
354
 
330
355
  It needs `claude` on PATH, Python 3, TypeScript (`tsc` on PATH, or set
331
- `TSC` to a `tsc`) and Claude Code's type declarations
356
+ `TSC` to a `tsc`; the check has passed on 5.9.3, 6.0.3 and 7.0.2, and no other
357
+ version has been tried) and Claude Code's type declarations
332
358
  (written to `.claude-plugin/types/claude-code/index.d.ts` when you start one
333
359
  session with `claude --plugin-dir .` from the clone, or set `CC_TYPES` to
334
360
  that file). A missing compiler or missing declarations fails the check with
335
361
  what to install; it never skips.
336
362
 
337
- That runs `claude plugin validate .`; `claude plugin test .`, 62 tests
363
+ That runs `claude plugin validate .`; `claude plugin test .`, 78 tests
338
364
  through the test runner Claude Code ships for plugins, with a mocked file
339
365
  system and mocked Claude Code events (the tests of the three former mods, one per
340
366
  option turned off, and tests where events finish out of order, arrive two at
@@ -350,13 +376,8 @@ fixture test fails on each (`tests/planted_bug_test.py`); and a check that the v
350
376
  plugin.json, marketplace.json, subagent_spend.py and CHANGELOG.md. It exits
351
377
  non-zero if any step fails. Changes are listed in [CHANGELOG.md](CHANGELOG.md).
352
378
 
353
- It needs the `claude` command, Python 3, and for the type check two more
354
- things, each failing with an install hint when missing: TypeScript
355
- (`tsc` on PATH, or set `TSC`; the check has passed on
356
- 5.9.3, 6.0.3 and 7.0.2, and no other version has been tried), and the
357
- declarations Claude Code writes beside a mod it loads from a folder (run
358
- `claude --plugin-dir .` once in the clone, or set `CC_TYPES` to that file).
359
-
360
379
  Built and maintained by Nightlab, an AI agent created by xnfinite ([@xnfinite](https://github.com/xnfinite)).
361
380
 
381
+ Nightlab on YouTube: [@NightlabBC](https://www.youtube.com/@NightlabBC).
382
+
362
383
  MIT license.
@@ -1,9 +1,10 @@
1
1
  import { atom, read, update } from 'claude-code'
2
- import type { EngineInterface, Register } from 'claude-code'
2
+ import type { EngineInterface, Register, SessionMeasureInput } from 'claude-code'
3
3
 
4
4
  import type { Row, Shown } from '../types'
5
- import { addStep, fmt, latest, nextStep, rowsText, paneLines, rows, spendStatus, types } from './spend'
5
+ import { addStep, count, fmt, latest, nextStep, rowsText, paneLines, rows, spendStatus, types } from './spend'
6
6
  import { countCall, localDay, tally, tallyText, turnsStatus } from './turns'
7
+ import { cap, capCommand, capOf, heldText, reached, warnNow } from './cap'
7
8
  import { OPEN_AT, bar, fileText, shownOf, shownStatus, usageLines, usageStatus } from './usage'
8
9
 
9
10
  // subagent-spend, the hooks module: a session's meters in one mod.
@@ -24,8 +25,11 @@ import { OPEN_AT, bar, fileText, shownOf, shownStatus, usageLines, usageStatus }
24
25
  // each part keeps its own piece and the line shows the pieces that have a reading, in the
25
26
  // order spend | usage | turns.
26
27
  //
27
- // DESIGN NOTE, deliberately narrow: it never denies, never rewrites a request or an event,
28
- // reads only the events the engine hands it and sends nothing off the machine. A failed
28
+ // - cap (option `capUsd`, off by default): at the cap the next model step is answered by the
29
+ // mod with no request made (./cap.ts).
30
+ //
31
+ // DESIGN NOTE, deliberately narrow: apart from the cap the user sets, it never denies, never rewrites a
32
+ // request or an event, reads only the events the engine hands it and sends nothing off the machine. A failed
29
33
  // write is swallowed (the spend part names it once in the status line): the files are
30
34
  // advisory, and a gauge that stops the engine would be worse than no gauge.
31
35
 
@@ -130,6 +134,9 @@ function showStatus($: EngineInterface, parts: Parts) {
130
134
 
131
135
  export const register: Register = (on, options) => {
132
136
  const usageOn = options.usageFile !== false
137
+ cap.usd = capOf(options.capUsd)
138
+ cap.spent = null
139
+ cap.warned = false
133
140
  const turnsOn = options.turnTally !== false
134
141
  const spendDir = String(options.dir || '.subagent-spend')
135
142
  const usageDir = String(options.usageFileDir || '.usage-file')
@@ -142,10 +149,17 @@ export const register: Register = (on, options) => {
142
149
  let spendWarned = false
143
150
  // Numbers each measurement in the order the engine raised them; latest.json is written from the newest.
144
151
  let seq = 0
152
+ /** The last measurement, whose windows and context the status line keeps between measurements. */
153
+ let measured: SessionMeasureInput | undefined
154
+ // A session's cost only grows (SessionCost: summed over every priced response), so the highest
155
+ // reading is the newest: a read that answers late with a lower figure never brings it back. Each
156
+ // session end starts a new count, and a read still out for the session left is dropped.
157
+ let costSession = 0
145
158
 
146
159
  on('session.start', async ($, e, next) => {
147
160
  try {
148
161
  await $.command.register({ name: 'subagent-spend', description: 'Show what each subagent of this session costs, in a pane' })
162
+ await $.command.register({ name: 'spend-cap', description: 'Show, set (/spend-cap 5) or lift (/spend-cap off) the spend cap of this session' })
149
163
  if (usageOn) await $.command.register({ name: 'usage-file', description: 'Show the rate-limit windows, context and cost of this session, in a pane' })
150
164
  } catch {
151
165
  // a surface with no commands; the panes still open by themselves
@@ -153,6 +167,8 @@ export const register: Register = (on, options) => {
153
167
  return next(e)
154
168
  })
155
169
 
170
+ on('command.run', { command: 'spend-cap' }, async (_, e) => ({ text: capCommand(e.args ?? '') }))
171
+
156
172
  on('command.run', { command: 'subagent-spend' }, async $ => {
157
173
  await $.ui.open({ id: SPEND_PANE, title: SPEND_TITLE })
158
174
  return { text: `Subagent spend pane opened. ${spendStatus()}` }
@@ -174,7 +190,7 @@ export const register: Register = (on, options) => {
174
190
  return (
175
191
  <Box flexDirection="column">
176
192
  <Text bold>
177
- {list.length} subagents, {fmt(tok)} tokens
193
+ {count(list.length, 'subagent')}, {fmt(tok)} tokens
178
194
  </Text>
179
195
  {list.length === 0 && <Text dimColor>No subagent has run in this session yet.</Text>}
180
196
  {paneLines(list)
@@ -231,9 +247,14 @@ export const register: Register = (on, options) => {
231
247
 
232
248
  on('session.end', async ($, e, next) => {
233
249
  startOver(e.sessionId)
250
+ // the next session's first measurement brings its own cost; the cap the user set stays
251
+ cap.spent = null
252
+ cap.warned = false
253
+ costSession++
234
254
  parts.spend = undefined
235
255
  // cost and context belong to the session left: the panes and the line wait for the new one's first reading
236
256
  parts.usage = undefined
257
+ measured = undefined
237
258
  showStatus($, parts)
238
259
  try {
239
260
  await update($, shownRows, () => [])
@@ -245,8 +266,18 @@ export const register: Register = (on, options) => {
245
266
  })
246
267
 
247
268
  on('session.measure', async ($, e, next) => {
269
+ if (e.cost) cap.spent = Math.max(cap.spent ?? 0, e.cost.usd)
270
+ const warning = warnNow()
271
+ if (warning) {
272
+ try {
273
+ $.ui.toast(warning)
274
+ } catch {
275
+ // a surface with no toasts: the cap still holds
276
+ }
277
+ }
248
278
  if (!usageOn) return next(e)
249
279
  const mine = ++seq
280
+ measured = e
250
281
  parts.usage = usageStatus(e)
251
282
  showStatus($, parts)
252
283
  try {
@@ -277,6 +308,30 @@ export const register: Register = (on, options) => {
277
308
  })
278
309
 
279
310
  on('turn.step', async function* ($, e, next) {
311
+ if (cap.usd !== null) {
312
+ // session.measure comes after a turn, not after each step: a tool loop or a subagent inside one
313
+ // turn is held only if the cost is read here, before each step
314
+ const session = costSession
315
+ try {
316
+ const usage = await $.session.usage()
317
+ const usd = session === costSession ? usage.cost?.usd : undefined
318
+ if (usd !== undefined && usd >= (cap.spent ?? 0)) cap.spent = usd
319
+ if (usd !== undefined && usd === cap.spent && usageOn && measured) {
320
+ // the line's dollar figure is the one the cap holds on, not the last turn's
321
+ parts.usage = usageStatus(measured, usd)
322
+ showStatus($, parts)
323
+ }
324
+ } catch {
325
+ // no cost here: the last measurement stands
326
+ }
327
+ }
328
+ if (reached()) {
329
+ // answered here, no request made: the turn (or the subagent) ends on this text
330
+ const text = heldText()
331
+ yield { kind: 'text' as const, index: 0, text }
332
+ yield { kind: 'stop' as const, stopReason: 'end_turn' as const, usage: null }
333
+ return { turnId: e.turnId, index: e.index, answer: text, toolUses: [], stopReason: 'end_turn' as const, usage: null }
334
+ }
280
335
  if (!e.agentId) {
281
336
  if (!turnsOn) return yield* next(e)
282
337
  // A main-loop step: counted as it starts, its file named and filled before any wait, so an
package/hooks/spend.ts CHANGED
@@ -89,10 +89,13 @@ export const label = (id: string, all: readonly string[]) => {
89
89
  return id
90
90
  }
91
91
 
92
+ /** `1 turn`, `2 turns`: a count with its word, singular for one. */
93
+ export const count = (n: number, word: string) => `${n} ${word}${n === 1 ? '' : 's'}`
94
+
92
95
  /** The pane's lines, biggest spender first: `Explore a1b2c3 8 turns 41k (out 2104, ctx 24k) claude-opus-5-5`. */
93
96
  export const paneLines = (list: Row[]) => {
94
97
  const ids = list.map(r => r.agent_id)
95
98
  return [...list]
96
99
  .sort((a, b) => b.total - a.total)
97
- .map(r => `${r.agent_type || 'agent'} ${label(r.agent_id, ids)} ${r.turns} turns ${fmt(r.total)} (out ${fmt(r.output)}, ctx ${fmt(r.context ?? 0)}) ${r.model}`)
100
+ .map(r => `${r.agent_type || 'agent'} ${label(r.agent_id, ids)} ${count(r.turns, 'turn')} ${fmt(r.total)} (out ${fmt(r.output)}, ctx ${fmt(r.context ?? 0)}) ${r.model}`)
98
101
  }
package/hooks/usage.ts CHANGED
@@ -61,10 +61,12 @@ export const fileText = (e: SessionMeasureInput, session: string, now: Date) =>
61
61
  ) + '\n'
62
62
 
63
63
  /** The status line part: only the figures that have a reading; undefined when none has. */
64
- export const usageStatus = (e: SessionMeasureInput) => {
64
+ export const usageStatus = (e: SessionMeasureInput, usd?: number) => {
65
65
  const parts = e.rateLimits.map(w => `${LABEL[w.kind] ?? w.kind} ${w.percentUsed}%`)
66
66
  if (e.context.percent !== undefined) parts.push(`ctx ${e.context.percent}%`)
67
- if (e.cost) parts.push(`$${e.cost.usd.toFixed(2)}`)
67
+ // `usd`: a cost read since that measurement (before a step, for the cap) replaces its figure
68
+ const shown = usd ?? e.cost?.usd
69
+ if (shown !== undefined) parts.push(`$${shown.toFixed(2)}`)
68
70
  return parts.length ? parts.join(' | ') : undefined
69
71
  }
70
72
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nightlab-agent/subagent-spend",
3
- "version": "0.7.2",
4
- "description": "Claude Code mod: per-subagent tokens, rate-limit windows, context fill, cost, steps, tool calls and tool errors of a session, in a notice under the prompt, local files and panes.",
3
+ "version": "0.8.0",
4
+ "description": "Claude Code mod: a spend cap for interactive sessions, and per-subagent tokens, rate-limit windows, context fill, cost, steps, tool calls and tool errors of a session, in a notice under the prompt, local files and panes (the usage pane not yet seen on a screen).",
5
5
  "keywords": [
6
6
  "claude-code",
7
7
  "claude-code-mod",
@@ -9,7 +9,10 @@
9
9
  "subagents",
10
10
  "token-usage",
11
11
  "rate-limits",
12
- "statusline"
12
+ "statusline",
13
+ "spend-cap",
14
+ "budget",
15
+ "max-budget-usd"
13
16
  ],
14
17
  "homepage": "https://github.com/nightlab-agent/subagent-spend#readme",
15
18
  "bugs": "https://github.com/nightlab-agent/subagent-spend/issues",
package/subagent_spend.py CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """subagent-spend 0.7.2: attribute Claude Code token usage to each subagent.
2
+ """subagent-spend 0.8.0: attribute Claude Code token usage to each subagent.
3
3
 
4
4
  subagent_spend.py hook SubagentStop hook: reads the hook JSON on stdin, sums the usage
5
5
  in the subagent transcript, appends one line to the session's rows file.