opencode-courier 0.13.8 → 0.14.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.
Files changed (163) hide show
  1. package/README.md +7 -9
  2. package/dist/index.d.ts +0 -8
  3. package/dist/index.js +6 -53
  4. package/dist/kernel/protocol.d.ts +0 -14
  5. package/dist/kernel/protocol.js +1 -15
  6. package/dist/kernel/tool.d.ts +0 -2
  7. package/dist/kernel/tool.js +0 -1
  8. package/dist/memory/domain.d.ts +6 -85
  9. package/dist/memory/domain.js +7 -63
  10. package/dist/memory/events.d.ts +0 -11
  11. package/dist/memory/events.js +1 -12
  12. package/dist/memory/forget.d.ts +0 -5
  13. package/dist/memory/forget.js +0 -4
  14. package/dist/memory/index.js +0 -2
  15. package/dist/memory/lessons.d.ts +0 -8
  16. package/dist/memory/lessons.js +0 -9
  17. package/dist/memory/note.d.ts +0 -9
  18. package/dist/memory/note.js +0 -5
  19. package/dist/memory/pin.d.ts +0 -5
  20. package/dist/memory/pin.js +0 -4
  21. package/dist/memory/ports.d.ts +2 -22
  22. package/dist/memory/recall.d.ts +0 -5
  23. package/dist/memory/recall.js +0 -12
  24. package/dist/memory/record.d.ts +0 -4
  25. package/dist/memory/record.js +8 -31
  26. package/dist/memory/rpc-handlers.d.ts +3 -6
  27. package/dist/memory/rpc-handlers.js +0 -6
  28. package/dist/memory/rpc.d.ts +6 -19
  29. package/dist/memory/rpc.js +1 -16
  30. package/dist/memory/store.d.ts +0 -5
  31. package/dist/memory/store.js +1 -16
  32. package/dist/memory/sweep.d.ts +0 -4
  33. package/dist/memory/sweep.js +0 -4
  34. package/dist/memory/text.d.ts +0 -25
  35. package/dist/memory/text.js +6 -36
  36. package/dist/memory/tools.d.ts +0 -2
  37. package/dist/memory/tools.js +0 -2
  38. package/dist/memory/tui.d.ts +1 -35
  39. package/dist/person/events.d.ts +0 -16
  40. package/dist/person/events.js +3 -30
  41. package/dist/person/form.d.ts +0 -9
  42. package/dist/person/form.js +0 -10
  43. package/dist/person/index.d.ts +0 -2
  44. package/dist/person/permission.d.ts +0 -34
  45. package/dist/person/permission.js +2 -28
  46. package/dist/person/ports.d.ts +0 -16
  47. package/dist/person/ports.js +0 -1
  48. package/dist/person/question/answer.d.ts +0 -28
  49. package/dist/person/question/answer.js +0 -49
  50. package/dist/person/question/index.js +0 -2
  51. package/dist/person/question/lifecycle.d.ts +0 -26
  52. package/dist/person/question/lifecycle.js +1 -32
  53. package/dist/person/question/pure.d.ts +0 -11
  54. package/dist/person/question/pure.js +0 -11
  55. package/dist/person/question/relay.d.ts +0 -1
  56. package/dist/person/question/relay.js +5 -67
  57. package/dist/person/question/runtime.d.ts +0 -14
  58. package/dist/person/question/runtime.js +1 -26
  59. package/dist/person/question/shared.d.ts +0 -17
  60. package/dist/person/question/shared.js +0 -20
  61. package/dist/person/question/types.d.ts +0 -44
  62. package/dist/person/rpc-handlers.d.ts +0 -7
  63. package/dist/person/rpc-handlers.js +0 -6
  64. package/dist/person/rpc.d.ts +0 -20
  65. package/dist/person/rpc.js +0 -16
  66. package/dist/person/text.d.ts +0 -45
  67. package/dist/person/text.js +2 -54
  68. package/dist/person/tools.d.ts +0 -3
  69. package/dist/person/tools.js +1 -3
  70. package/dist/person/tui.d.ts +0 -21
  71. package/dist/platform/bounded.d.ts +0 -8
  72. package/dist/platform/bounded.js +0 -8
  73. package/dist/platform/events.d.ts +0 -18
  74. package/dist/platform/events.js +3 -22
  75. package/dist/platform/hub.d.ts +0 -113
  76. package/dist/platform/hub.js +1 -76
  77. package/dist/platform/json.d.ts +0 -3
  78. package/dist/platform/json.js +1 -3
  79. package/dist/platform/storage.d.ts +0 -7
  80. package/dist/platform/storage.js +0 -6
  81. package/dist/platform/version.d.ts +0 -9
  82. package/dist/platform/version.js +0 -8
  83. package/dist/rpc.js +0 -1
  84. package/dist/scheduling/index.d.ts +0 -1
  85. package/dist/scheduling/index.js +0 -1
  86. package/dist/scheduling/later.d.ts +0 -11
  87. package/dist/scheduling/later.js +0 -14
  88. package/dist/scheduling/text.d.ts +0 -1
  89. package/dist/scheduling/text.js +0 -1
  90. package/dist/scheduling/tools.d.ts +0 -1
  91. package/dist/scheduling/tools.js +1 -5
  92. package/dist/skill.d.ts +1 -9
  93. package/dist/skill.js +4 -12
  94. package/dist/tree/cleanup.d.ts +0 -20
  95. package/dist/tree/cleanup.js +0 -26
  96. package/dist/tree/context.d.ts +0 -5
  97. package/dist/tree/context.js +0 -8
  98. package/dist/tree/domain.d.ts +0 -64
  99. package/dist/tree/domain.js +7 -45
  100. package/dist/tree/events.d.ts +0 -36
  101. package/dist/tree/events.js +4 -49
  102. package/dist/tree/forget.d.ts +0 -15
  103. package/dist/tree/forget.js +0 -18
  104. package/dist/tree/group.d.ts +0 -22
  105. package/dist/tree/group.js +1 -37
  106. package/dist/tree/index.d.ts +2 -2
  107. package/dist/tree/index.js +1 -3
  108. package/dist/tree/limits.d.ts +0 -4
  109. package/dist/tree/limits.js +1 -12
  110. package/dist/tree/mascot.d.ts +0 -25
  111. package/dist/tree/mascot.js +1 -32
  112. package/dist/tree/model.d.ts +0 -5
  113. package/dist/tree/model.js +2 -15
  114. package/dist/tree/nudge.d.ts +0 -11
  115. package/dist/tree/nudge.js +3 -16
  116. package/dist/tree/panel.d.ts +0 -8
  117. package/dist/tree/panel.js +3 -16
  118. package/dist/tree/ports.d.ts +2 -138
  119. package/dist/tree/report.d.ts +0 -10
  120. package/dist/tree/report.js +0 -11
  121. package/dist/tree/roster.d.ts +0 -21
  122. package/dist/tree/roster.js +0 -21
  123. package/dist/tree/rpc-handlers.d.ts +6 -1
  124. package/dist/tree/rpc-handlers.js +0 -5
  125. package/dist/tree/rpc.d.ts +9 -109
  126. package/dist/tree/rpc.js +2 -38
  127. package/dist/tree/running.d.ts +0 -6
  128. package/dist/tree/running.js +2 -10
  129. package/dist/tree/send.d.ts +0 -4
  130. package/dist/tree/send.js +4 -31
  131. package/dist/tree/spawn.d.ts +0 -7
  132. package/dist/tree/spawn.js +6 -47
  133. package/dist/tree/standing.d.ts +0 -8
  134. package/dist/tree/standing.js +1 -11
  135. package/dist/tree/status.d.ts +0 -8
  136. package/dist/tree/status.js +1 -9
  137. package/dist/tree/stop.d.ts +2 -9
  138. package/dist/tree/stop.js +58 -25
  139. package/dist/tree/store.d.ts +0 -8
  140. package/dist/tree/store.js +4 -38
  141. package/dist/tree/subtree.d.ts +8 -26
  142. package/dist/tree/subtree.js +37 -35
  143. package/dist/tree/summary.d.ts +0 -4
  144. package/dist/tree/summary.js +0 -8
  145. package/dist/tree/text.d.ts +1 -44
  146. package/dist/tree/text.js +5 -65
  147. package/dist/tree/tools.d.ts +0 -6
  148. package/dist/tree/tools.js +4 -8
  149. package/dist/tree/tui.d.ts +0 -81
  150. package/dist/tree/wake.d.ts +0 -30
  151. package/dist/tree/wake.js +5 -39
  152. package/dist/tui.js +22 -8
  153. package/package.json +8 -9
  154. package/dist/webhook/index.d.ts +0 -5
  155. package/dist/webhook/index.js +0 -3
  156. package/dist/webhook/receiver.d.ts +0 -80
  157. package/dist/webhook/receiver.js +0 -339
  158. package/dist/webhook/subscriptions.d.ts +0 -29
  159. package/dist/webhook/subscriptions.js +0 -52
  160. package/dist/webhook/text.d.ts +0 -23
  161. package/dist/webhook/text.js +0 -82
  162. package/dist/webhook/tools.d.ts +0 -19
  163. package/dist/webhook/tools.js +0 -46
@@ -1,13 +1,7 @@
1
- /** Digby's frames take one panel frame in this many, about every 450 ms. */
2
1
  export const MASCOT_EVERY = 5;
3
- /** He blinks on every this-many-th of his frames where his eyes are open. */
4
2
  export const BLINK_EVERY = 11;
5
- /** Running, he is still and awake for this many of his frames, about 8 s, then digs for one cycle of six. */
6
3
  export const DIG_EVERY = 18;
7
- /**
8
- * The 8×4 glyph's frames by state, pixel rows top to bottom; a state with several cycles through them, one per mascot frame.
9
- * No muted pixel touches the mound, here or in the 16×16, as `text.muted` is close to `background.raised.max`.
10
- */
4
+ // text.muted is close to background.raised.max: no muted pixel on the mound
11
5
  export const GLYPH = {
12
6
  idle: [["........", "........", "..gggg..", "gggggggg"]],
13
7
  spawn: [
@@ -33,9 +27,7 @@ export const GLYPH = {
33
27
  ],
34
28
  gone: [["........", ".mmmmmm.", ".mkmmkm.", "mmmmmmmm"]],
35
29
  };
36
- /** The states in which his eyes are open, so he blinks. */
37
30
  const OPEN_EYES = new Set(["waiting", "report", "running"]);
38
- /** The 8×4 glyph's pixels for `state` at mascot frame `frame`, its first frame with none: animations off. */
39
31
  export function glyph(state, frame) {
40
32
  const frames = GLYPH[state];
41
33
  const pixels = frames[(frame ?? 0) % frames.length];
@@ -45,10 +37,8 @@ export function glyph(state, frame) {
45
37
  return [...pixels.slice(0, 2), ".gggggg.", ...pixels.slice(3)];
46
38
  return OPEN_EYES.has(state) && blinks(frame) ? pixels.map((row) => row.replaceAll("k", "b")) : pixels;
47
39
  }
48
- /** The mound's top row widens for two frames in four. */
49
40
  const breathesOut = (frame) => Math.floor(frame / 2) % 2 === 1;
50
41
  const blinks = (frame) => frame % BLINK_EVERY === BLINK_EVERY - 1;
51
- /** Pixel rows, two to a line of cells, by the half-block rule; an odd last row has nothing under it. */
52
42
  export function cells(pixels) {
53
43
  const lines = [];
54
44
  for (let row = 0; row < pixels.length; row += 2) {
@@ -68,14 +58,8 @@ function cell(top, bottom) {
68
58
  return { char: "█", fg: top };
69
59
  return bottom ? { char: "▀", fg: top, bg: bottom } : { char: "▀", fg: top };
70
60
  }
71
- /** The 16×16's size, in pixels. */
72
61
  const SIZE = 16;
73
- /** How far each of the head's rows leans right when he slumps. */
74
62
  const SLUMP = [2, 2, 2, 1, 1, 0, 0];
75
- /**
76
- * The 16×16 Digby, his head raised by `rise` (0 in the hole, 1 fully up; below 0 out of sight) over a body in the hole,
77
- * then the mound (its top row `top` wide, `shift` columns over) and the ground, both `mound`, drawn over it but for the hole.
78
- */
79
63
  export function digby(rise, { top = 6, eyes = "open", slump = false, shift = 0, mound = "g" } = {}) {
80
64
  const grid = Array.from({ length: SIZE }, () => new Array(SIZE).fill("."));
81
65
  const span = (row, width, pixel, skip = (_column) => false, by = 0) => {
@@ -104,7 +88,6 @@ export function digby(rise, { top = 6, eyes = "open", slump = false, shift = 0,
104
88
  span(row, SIZE, mound);
105
89
  return grid.map((row) => row.join(""));
106
90
  }
107
- /** `pixels` with `spots` drawn over them. */
108
91
  export function paint(pixels, spots) {
109
92
  const grid = pixels.map((row) => [...row]);
110
93
  for (const [row, column, drawn] of spots)
@@ -114,42 +97,32 @@ export function paint(pixels, spots) {
114
97
  });
115
98
  return grid.map((row) => row.join(""));
116
99
  }
117
- /** A column of `length` pixels of `pixel` from row `row`. */
118
100
  const post = (row, column, length, pixel) => Array.from({ length }, (_, at) => [row + at, column, pixel]);
119
- /** The head's top row at `rise`. */
120
101
  const headTop = (rise) => Math.round(10 - 8 * rise);
121
- /** Up and awake at `rise`, his eyes `shift` columns over (0 straight ahead) when `open`. */
122
102
  const awake = (rise, shift) => (open) => {
123
103
  const row = headTop(rise) + 3;
124
104
  return paint(digby(rise, { eyes: "open" }), [[row, 6, "b"], [row, 9, "b"], ...(open ? [[row, 6 + shift, "k"], [row, 9 + shift, "k"]] : [])]);
125
105
  };
126
- /** Where a clod flies on each side, frame by frame: out of the hole, up, over and down, a row clear of the mound's rim. */
127
106
  const CLOD_LEFT = [[8, 3], [7, 2], [6, 1], [5, 0], [7, 0], [9, 0]];
128
107
  const CLOD_RIGHT = [[8, 12], [7, 13], [6, 14], [5, 15], [7, 15], [9, 15]];
129
108
  const CLODS = [[CLOD_LEFT, 0], [CLOD_LEFT, 3], [CLOD_RIGHT, 1], [CLOD_RIGHT, 4]];
130
- /** Digging at frame `at`: up with his eyes open, the mound still, four clods on their arcs, one always in the air. */
131
109
  const digging = (at) => (open) => paint(digby(0.6, { eyes: open ? "open" : "none" }), CLODS.map(([path, phase]) => {
132
110
  const [row, column] = path[(at + phase) % path.length];
133
111
  return [row, column, "m"];
134
112
  }));
135
- /** Running: still and awake, a glance left then right over twelve frames, then one dig cycle; the mound never moves. */
136
113
  const running = [
137
114
  ...Array.from({ length: DIG_EVERY }, (_, at) => [0, 0, 0, 0, 0, -1, -1, 0, 0, 0, 1, 1][at % 12]).map((shift) => awake(0.6, shift)),
138
115
  ...Array.from({ length: CLOD_LEFT.length }, (_, at) => digging(at)),
139
116
  ];
140
- /** A child's head peeking out of a side mound at columns `column`–`column + 2`, `up` or half sunk. */
141
117
  const peek = (column, up) => [
142
118
  ...(up ? [[8, column, ".b."], [9, column, "bkb"]] : [[9, column, ".b."]]),
143
119
  [10, column, "gbg"],
144
120
  [11, column, "ggg"],
145
121
  ];
146
122
  const spawn = (up, dirt) => paint(digby(0.1), [...peek(0, up), ...peek(13, up), [12, 0, "gg"], [12, 14, "gg"], ...dirt]);
147
- /** The sign on its post, which stops a pixel above the ground so the muted post does not sit on it. */
148
123
  const sign = (shown) => [...(shown ? [[2, 13, "www"], [3, 13, "wkw"], [4, 13, "www"]] : []), ...post(5, 14, 7, "m")];
149
- /** His paws, held a pixel above the mound's rim so the muted pixels do not touch it. */
150
124
  const waiting = (shown) => (open) => paint(digby(1, { eyes: open ? "wide" : "none" }), [[8, 4, "mm"], [8, 10, "mm"], ...sign(shown)]);
151
125
  const stalled = (dots) => () => paint(digby(0.7, { eyes: "shut", slump: true }), [[5, 0, dots]]);
152
- /** The letter held up on his arm, which stops a pixel above the mound so it does not touch it, and the spark beside him. */
153
126
  const report = (rise, spark) => (open) => paint(digby(rise, { eyes: open ? "open" : "none" }), [
154
127
  [0, 12, "aaaa"],
155
128
  [1, 12, "akka"],
@@ -157,7 +130,6 @@ const report = (rise, spark) => (open) => paint(digby(rise, { eyes: open ? "open
157
130
  ...post(3, 13, 8, "m"),
158
131
  ...spark.map((row, at) => [1 + at, 0, row]),
159
132
  ]);
160
- /** The 16×16's frames by state but idle, each its pixels with the eyes open or, for a blink, not; the first reads alone. */
161
133
  export const ART = {
162
134
  spawn: [
163
135
  () => spawn(true, [[3, 1, "m"], [5, 3, "m"], [2, 14, "m"], [5, 12, "m"]]),
@@ -169,9 +141,7 @@ export const ART = {
169
141
  report: [report(1, [".s.", "sss", ".s."]), report(1.25, ["s.s", ".s.", "s.s"]), report(1.1, [".s.", "sss", ".s."])],
170
142
  gone: [() => digby(-0.3, { mound: "m" })],
171
143
  };
172
- /** Where idle's `z` drifts up, by cell line, one step per mascot frame, then gone a frame. */
173
144
  const Z_LINES = [3, 2, 1, undefined];
174
- /** The 16×16 idle Digby at mascot frame `frame`, its first with none: the mound breathing, a `z` drifting up beside it. */
175
145
  export function idleArt(frame) {
176
146
  const lines = cells(digby(-0.3, { top: frame !== undefined && breathesOut(frame) ? 8 : 6 }));
177
147
  const line = Z_LINES[(frame ?? 0) % Z_LINES.length];
@@ -179,7 +149,6 @@ export function idleArt(frame) {
179
149
  return lines;
180
150
  return lines.map((cellsOf, at) => (at === line ? cellsOf.map((one, column) => (column === 13 ? { char: "z", fg: "m" } : one)) : cellsOf));
181
151
  }
182
- /** The 16×16 Digby for `state` at mascot frame `frame`, its first frame with none: animations off. */
183
152
  export function art(state, frame) {
184
153
  if (state === "idle")
185
154
  return idleArt(frame);
@@ -1,14 +1,9 @@
1
1
  import { type ModelRef } from "./domain.js";
2
2
  import type { CourierPorts } from "./ports.js";
3
- /** What courier_spawn says of the model: `provider/model`, and the variant of whichever model is chosen. */
4
3
  export interface ModelChoice {
5
4
  readonly model?: string;
6
5
  readonly variant?: string;
7
6
  }
8
- /**
9
- * The model a child runs on: the `model` named, else the agent's, else the one its parent runs on, with the `variant`
10
- * named; `parent` is the parent's own, for the roster to tell a child on another one.
11
- */
12
7
  export declare function chooseModel(ports: CourierPorts, parentID: string, choice: ModelChoice, agent: string | undefined): Promise<{
13
8
  model: undefined;
14
9
  parent: ModelRef | undefined;
@@ -1,18 +1,13 @@
1
1
  import { modelName, parseModelName } from "./domain.js";
2
2
  import { modelFormatRefusal, unknownModelRefusal, unknownVariantRefusal, variantWithoutModelRefusal } from "./text.js";
3
- /**
4
- * The model a child runs on: the `model` named, else the agent's, else the one its parent runs on, with the `variant`
5
- * named; `parent` is the parent's own, for the roster to tell a child on another one.
6
- */
7
3
  export async function chooseModel(ports, parentID, choice, agent) {
8
- // Some models send null, or an empty string, for an optional field they leave out.
9
4
  const named = trimmed(choice.model);
10
5
  const variant = trimmed(choice.variant);
11
6
  const own = named === undefined ? undefined : parseModelName(named);
12
7
  if (named !== undefined && !own)
13
8
  throw new Error(modelFormatRefusal(named));
14
9
  const parent = await parentModel(ports, parentID);
15
- // An agent that cannot be looked up may name a model of its own, which OpenCode applies when none is passed.
10
+ // with no model passed, OpenCode applies the agent's own (api-notes)
16
11
  const inherited = own ? undefined : await inheritedModel(ports, parent, agent).catch(() => undefined);
17
12
  const base = own ?? inherited;
18
13
  if (!base) {
@@ -26,27 +21,19 @@ export async function chooseModel(ports, parentID, choice, agent) {
26
21
  await check(ports, model, own !== undefined);
27
22
  return { model, parent };
28
23
  }
29
- /**
30
- * The model the parent runs on: its session's, else OpenCode's default. A failed lookup must not keep a child on no
31
- * particular model from starting, so it reads as none, and the child then runs on OpenCode's default.
32
- */
33
24
  async function parentModel(ports, parentID) {
34
25
  const session = await ports.session.get({ sessionID: parentID }).catch(() => undefined);
35
26
  if (!session)
36
27
  return undefined;
37
28
  return session.model ?? (await ports.defaultModel().catch(() => undefined));
38
29
  }
39
- /** The named agent's model, if it names one; else the parent's. */
40
30
  async function inheritedModel(ports, parent, agent) {
41
31
  if (!agent)
42
32
  return parent;
43
33
  const named = await ports.agent.get({ agentID: agent, location: { directory: ports.directory } });
44
34
  return named.data.model ?? parent;
45
35
  }
46
- /**
47
- * Refuses a model OpenCode does not list, or a variant its model has not; `"default"` is every model's. A listing that
48
- * cannot be read checks nothing: OpenCode refuses what it does not know at the child's first turn, which is reported.
49
- */
36
+ // OpenCode refuses an unknown model at the child's first turn (api-notes)
50
37
  async function check(ports, model, named) {
51
38
  let known;
52
39
  try {
@@ -1,17 +1,11 @@
1
1
  import type { ChildStore, Waits, WatchPorts } from "./ports.js";
2
- /** OpenCode's `experimental.idle` hook event: a session about to go idle, and the background work still running for it or below it. */
3
2
  export interface Idle {
4
3
  readonly sessionID: string;
5
4
  readonly background: ReadonlyArray<{
6
5
  readonly sessionID: string;
7
6
  }>;
8
7
  }
9
- /** The ports the reminder is sent through: the watcher's. */
10
8
  export type NudgePorts = Pick<WatchPorts, "storage" | "session" | "permissions" | "now" | "log">;
11
- /**
12
- * The report a spawned session owes since its last prompt, unless it waits or courier_stop stopped it since: what the
13
- * silent-turn notice tells the parent of, and the reminder reminds the session of.
14
- */
15
9
  export declare function owedUnwaited(ports: NudgePorts, children: ChildStore, outside: Waits, sessionID: string): Promise<{
16
10
  resumesAt?: number | undefined;
17
11
  prompt: number;
@@ -19,10 +13,5 @@ export declare function owedUnwaited(ports: NudgePorts, children: ChildStore, ou
19
13
  progressed: boolean;
20
14
  ended: boolean;
21
15
  } | undefined>;
22
- /**
23
- * Reminds a spawned session about to go idle owing a report it does not wait on, once per prompt, so it reports in the
24
- * same execution; one with background work of its own is woken again by it. Returns whether it was reminded.
25
- */
26
16
  export declare function nudgeSilent(ports: NudgePorts, seen: Set<string>, outside: Waits, idle: Idle): Promise<boolean>;
27
- /** `nudgeSilent` as the hook runs it: a hook that fails fails the execution, so a failure is logged, never thrown. */
28
17
  export declare function onIdle(ports: NudgePorts, seen: Set<string>, outside: Waits, idle: Idle): Promise<boolean>;
@@ -4,24 +4,15 @@ import { hasCompleteGroup } from "./group.js";
4
4
  import { awaited, reportOf, stoppedSincePrompt } from "./report.js";
5
5
  import { childStore, groupStore } from "./store.js";
6
6
  import { reportReminder } from "./text.js";
7
- /**
8
- * Whether a session prompted at `prompt` waits: on an untold report of a session it started, a complete group of
9
- * its about to be delivered, or what `outside` says it waits on. Cheapest first.
10
- */
11
7
  async function waits(ports, outside, sessionID, prompt) {
12
8
  const children = childStore(ports.storage);
13
9
  const started = await children.children(sessionID);
14
10
  if ((await Promise.all(started.map((entry) => awaited(children, entry.sessionID)))).includes(true))
15
11
  return true;
16
- // A group with a member still out is waited on through that member, above; one whose every out member was told of is not.
17
12
  if (started.some((entry) => entry.group) && (await hasCompleteGroup(groupStore(ports.storage), sessionID)))
18
13
  return true;
19
14
  return outside(ports, sessionID, prompt);
20
15
  }
21
- /**
22
- * The report a spawned session owes since its last prompt, unless it waits or courier_stop stopped it since: what the
23
- * silent-turn notice tells the parent of, and the reminder reminds the session of.
24
- */
25
16
  export async function owedUnwaited(ports, children, outside, sessionID) {
26
17
  const report = await reportOf(children, sessionID);
27
18
  if (!report?.owes || (await waits(ports, outside, sessionID, report.prompt)))
@@ -30,10 +21,6 @@ export async function owedUnwaited(ports, children, outside, sessionID) {
30
21
  return undefined;
31
22
  return report;
32
23
  }
33
- /**
34
- * Reminds a spawned session about to go idle owing a report it does not wait on, once per prompt, so it reports in the
35
- * same execution; one with background work of its own is woken again by it. Returns whether it was reminded.
36
- */
37
24
  export async function nudgeSilent(ports, seen, outside, idle) {
38
25
  const { sessionID } = idle;
39
26
  if (idle.background.some((work) => work.sessionID === sessionID))
@@ -49,7 +36,7 @@ export async function nudgeSilent(ports, seen, outside, idle) {
49
36
  if ((nudgedAt !== undefined && nudgedAt >= report.prompt) || !claim(seen, `nudge/${sessionID}/${report.prompt}`))
50
37
  return false;
51
38
  const at = Math.max(ports.now(), report.prompt);
52
- // Noted before it is sent, so a second hook never sends another, and with its id after, before the turn takes it.
39
+ // every instance's idle hook runs; delivery matches by inbox id (api-notes)
53
40
  await children.nudged(sessionID, at);
54
41
  const { id } = await ports.session.synthetic({
55
42
  sessionID,
@@ -57,13 +44,13 @@ export async function nudgeSilent(ports, seen, outside, idle) {
57
44
  description: "Reminded to report",
58
45
  metadata: { source: "courier", reminder: "report", resume: false },
59
46
  delivery: "steer",
60
- // The hook's own execution takes it: no wake, and the missed-wake watch leaves it be.
47
+ // input the idle hook admits continues its execution (api-notes)
61
48
  resume: false,
62
49
  });
63
50
  await children.nudged(sessionID, at, id);
64
51
  return true;
65
52
  }
66
- /** `nudgeSilent` as the hook runs it: a hook that fails fails the execution, so a failure is logged, never thrown. */
53
+ // a throwing idle hook fails the execution (api-notes)
67
54
  export async function onIdle(ports, seen, outside, idle) {
68
55
  try {
69
56
  return await nudgeSilent(ports, seen, outside, idle);
@@ -1,20 +1,12 @@
1
1
  import type { CourierPorts, ModelWindow, Pending, ShownForm } from "./ports.js";
2
2
  import type { Tree, Wait } from "./rpc.js";
3
3
  type Messages = Awaited<ReturnType<CourierPorts["session"]["context"]>>;
4
- /** The last tool call in a session's messages, and when it was made. */
5
4
  export declare function lastTool(messages: Messages): {
6
5
  name: string;
7
6
  at: number;
8
7
  } | undefined;
9
- /** How full a session's context is, as OpenCode's session view reads it (packages/tui/src/util/session.ts, v2.1.0): the last usage since the last compaction over its model's window. */
10
8
  export declare function contextOf(messages: Messages, windows: ReadonlyArray<ModelWindow>): number | undefined;
11
- /** A request as the panel says what a paused session waits on: a permission request, or its first question. */
12
9
  export declare const waitOf: (pending: Pending) => Wait;
13
- /** A form a session shows, as the panel says what it waits on. */
14
10
  export declare const formWait: (form: ShownForm) => Wait;
15
- /**
16
- * The tree panel's rows: the sessions under a session as courier_tree lists them, at most `maxTotal`, live ones first,
17
- * then the newest, each as `row` reads it; `top` the session itself, `truncated` the ones left out, `stalled` its stall.
18
- */
19
11
  export declare function panel(ports: CourierPorts, rootID: string): Promise<Tree>;
20
12
  export {};
@@ -4,7 +4,6 @@ import { standingIn } from "./status.js";
4
4
  import { childStore } from "./store.js";
5
5
  import { readTop, readTree } from "./subtree.js";
6
6
  import { stalledAmong } from "./wake.js";
7
- /** The last tool call in a session's messages, and when it was made. */
8
7
  export function lastTool(messages) {
9
8
  for (const message of messages.toReversed()) {
10
9
  if (message.type !== "assistant")
@@ -15,7 +14,7 @@ export function lastTool(messages) {
15
14
  }
16
15
  return undefined;
17
16
  }
18
- /** How full a session's context is, as OpenCode's session view reads it (packages/tui/src/util/session.ts, v2.1.0): the last usage since the last compaction over its model's window. */
17
+ // as OpenCode's session view reads it (packages/tui/src/util/session.ts)
19
18
  export function contextOf(messages, windows) {
20
19
  const compacted = messages.findLastIndex((message) => message.type === "compaction" && message.status === "completed");
21
20
  const last = messages.findLast((message, index) => index > compacted && message.type === "assistant" && message.tokens !== undefined);
@@ -26,13 +25,10 @@ export function contextOf(messages, windows) {
26
25
  const window = windows.find((model) => model.providerID === last.model.providerID && model.id === last.model.id)?.context;
27
26
  return used > 0 && window ? used / window : undefined;
28
27
  }
29
- /** A request as the panel says what a paused session waits on: a permission request, or its first question. */
30
28
  export const waitOf = (pending) => pending.type === "permission"
31
29
  ? { type: "permission", action: pending.action, resources: [...pending.resources] }
32
30
  : { type: "question", question: pending.questions[0]?.question ?? "" };
33
- /** A form a session shows, as the panel says what it waits on. */
34
31
  export const formWait = (form) => ({ type: "form", title: form.title });
35
- /** A session's report since its last prompt, as the panel shows it; none for one spawned before this was kept. */
36
32
  function reportRow(report, status) {
37
33
  if (!report)
38
34
  return {};
@@ -42,10 +38,6 @@ function reportRow(report, status) {
42
38
  return { report: { state: "ended", progressed: report.progressed } };
43
39
  return { report: { state: "reported", progressed: report.progressed, ...(status ? { status } : {}) } };
44
40
  }
45
- /**
46
- * What a session does: waiting on a request to the person or a form it shows them, stalled, running, idle, or gone when
47
- * OpenCode no longer knows it. A message waiting in its inbox is no turn (`runs`); a stalled one reads stalled.
48
- */
49
41
  function stateOf(info, waits, stalled, running) {
50
42
  if (!info)
51
43
  return "gone";
@@ -55,8 +47,7 @@ function stateOf(info, waits, stalled, running) {
55
47
  return "stalled";
56
48
  return running ? "running" : "idle";
57
49
  }
58
- /** One session of the panel: what it is doing, its report and group standing, its last tool call, context use and cost. */
59
- async function row(ports, windows, { entry, depth, info, running, endedAt, pending, forms, report }, stalled) {
50
+ async function row(ports, windows, { entry, depth, subagent, info, running, endedAt, pending, forms, report }, stalled) {
60
51
  const { sessionID } = entry;
61
52
  const [messages, status, standing, stopped] = await Promise.all([
62
53
  ports.session.context({ sessionID }).catch(() => []),
@@ -67,7 +58,6 @@ async function row(ports, windows, { entry, depth, info, running, endedAt, pendi
67
58
  const tool = lastTool(messages);
68
59
  const context = contextOf(messages, windows);
69
60
  const state = stateOf(info, pending.length + forms.length, stalled !== undefined, running);
70
- // A prompt since the failure drops it from the report, so `resumesAt` here is that of the last turn alone.
71
61
  const resumes = state === "idle" && !stopped && report?.resumesAt !== undefined && report.resumesAt > ports.now() ? report.resumesAt : undefined;
72
62
  return {
73
63
  sessionID,
@@ -82,6 +72,7 @@ async function row(ports, windows, { entry, depth, info, running, endedAt, pendi
82
72
  ...reportRow(report, status),
83
73
  ...(entry.group && standing ? { group: { name: entry.group, standing } } : {}),
84
74
  ...startedAs(entry),
75
+ ...(subagent ? { subagent } : {}),
85
76
  created: info?.time.created ?? entry.createdAt,
86
77
  ...(info && !running && endedAt !== undefined ? { idle: endedAt } : {}),
87
78
  ...(info ? { cost: info.cost } : {}),
@@ -90,10 +81,6 @@ async function row(ports, windows, { entry, depth, info, running, endedAt, pendi
90
81
  waits: [...pending.map(waitOf), ...forms.map(formWait)],
91
82
  };
92
83
  }
93
- /**
94
- * The tree panel's rows: the sessions under a session as courier_tree lists them, at most `maxTotal`, live ones first,
95
- * then the newest, each as `row` reads it; `top` the session itself, `truncated` the ones left out, `stalled` its stall.
96
- */
97
84
  export async function panel(ports, rootID) {
98
85
  const [{ kept, leftOut }, root] = await Promise.all([readTree(ports, rootID), readTop(ports, rootID)]);
99
86
  const stalled = await stalledAmong(ports, [rootID, ...kept.map((node) => node.entry.sessionID)]);