toolroll 0.9.59 → 0.9.61

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 (152) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +8 -6
  3. package/dist/agent-board.d.ts +208 -0
  4. package/dist/agent-board.js +454 -0
  5. package/dist/agent-fence.d.ts +19 -0
  6. package/dist/agent-fence.js +100 -8
  7. package/dist/agent-pulse.d.ts +43 -0
  8. package/dist/agent-pulse.js +214 -0
  9. package/dist/agent-turns.d.ts +52 -0
  10. package/dist/agent-turns.js +137 -0
  11. package/dist/agents-board.d.ts +101 -0
  12. package/dist/agents-board.js +225 -0
  13. package/dist/assignment-adapters.d.ts +2 -1
  14. package/dist/assignment-adapters.js +1 -1
  15. package/dist/assignment-delivery.d.ts +16 -0
  16. package/dist/assignment-delivery.js +54 -28
  17. package/dist/assignment-status.d.ts +14 -5
  18. package/dist/assignment-status.js +70 -39
  19. package/dist/assignment.d.ts +10 -2
  20. package/dist/assignment.js +21 -14
  21. package/dist/bin.js +11 -2
  22. package/dist/brief-fence.d.ts +9 -0
  23. package/dist/brief-fence.js +22 -0
  24. package/dist/browser/workspace.css +2 -2
  25. package/dist/browser/workspace.js +29 -25
  26. package/dist/browser-crew.js +2 -0
  27. package/dist/browser-workspace.d.ts +79 -1
  28. package/dist/browser-workspace.js +1 -0
  29. package/dist/builder.d.ts +8 -0
  30. package/dist/builder.js +178 -29
  31. package/dist/chat-channel.js +10 -1
  32. package/dist/chat-core.d.ts +6 -0
  33. package/dist/chat-core.js +8 -0
  34. package/dist/chat-delivery.js +22 -0
  35. package/dist/chat-messages.js +8 -1
  36. package/dist/chat-quiet.js +1 -0
  37. package/dist/chat-voice.d.ts +2 -0
  38. package/dist/chat-voice.js +2 -0
  39. package/dist/checkout-cleanup.d.ts +3 -0
  40. package/dist/checkout-cleanup.js +11 -2
  41. package/dist/cli-fast.d.ts +8 -0
  42. package/dist/cli-fast.js +18 -0
  43. package/dist/cli-help.d.ts +5 -0
  44. package/dist/cli-help.js +91 -0
  45. package/dist/cli.d.ts +6 -5
  46. package/dist/cli.js +17 -96
  47. package/dist/contracts/console-api.d.ts +3 -1
  48. package/dist/contracts/console-api.js +5 -1
  49. package/dist/contracts/lead-tools.d.ts +16 -2
  50. package/dist/contracts/lead-tools.js +5 -1
  51. package/dist/design/tokens.css +234 -0
  52. package/dist/desktop-update.d.ts +1 -1
  53. package/dist/discord.js +2 -1
  54. package/dist/evidence-pack.js +1 -1
  55. package/dist/evidence.d.ts +28 -0
  56. package/dist/evidence.js +87 -17
  57. package/dist/exec.d.ts +32 -0
  58. package/dist/exec.js +229 -10
  59. package/dist/flow-actions.js +2 -1
  60. package/dist/guides.js +14 -11
  61. package/dist/invoke.js +110 -18
  62. package/dist/landed-cleanup.d.ts +157 -0
  63. package/dist/landed-cleanup.js +707 -0
  64. package/dist/lead-cli.js +22 -19
  65. package/dist/lead-contract.js +1 -1
  66. package/dist/lead-doors.js +49 -2
  67. package/dist/lead-follow.d.ts +2 -0
  68. package/dist/lead-follow.js +138 -88
  69. package/dist/lead-tools.js +48 -8
  70. package/dist/live-input.d.ts +68 -0
  71. package/dist/live-input.js +254 -0
  72. package/dist/live.d.ts +1 -1
  73. package/dist/live.js +4 -2
  74. package/dist/mailbox.js +3 -2
  75. package/dist/maintenance.d.ts +2 -0
  76. package/dist/maintenance.js +5 -2
  77. package/dist/mcp-core.js +1 -1
  78. package/dist/operate-remote-arguments.js +5 -1
  79. package/dist/operate-remote.js +1 -1
  80. package/dist/operate.d.ts +1 -1
  81. package/dist/operate.js +201 -19
  82. package/dist/orphan-sweep.d.ts +2 -0
  83. package/dist/orphan-sweep.js +5 -3
  84. package/dist/project-delete-ui.d.ts +11 -0
  85. package/dist/project-delete-ui.js +29 -2
  86. package/dist/project-tools.d.ts +7 -11
  87. package/dist/project-tools.js +13 -37
  88. package/dist/provider.d.ts +73 -8
  89. package/dist/provider.js +87 -17
  90. package/dist/result-completion.d.ts +7 -0
  91. package/dist/result-completion.js +9 -0
  92. package/dist/runner.d.ts +4 -2
  93. package/dist/runner.js +3 -3
  94. package/dist/serve.js +22 -3
  95. package/dist/server/chrome.d.ts +6 -3
  96. package/dist/server/chrome.js +8 -4
  97. package/dist/server/console.css +9 -64
  98. package/dist/server/live.d.ts +3 -0
  99. package/dist/server/live.js +35 -2
  100. package/dist/server/pages.js +59 -35
  101. package/dist/server/render-chat.js +14 -2
  102. package/dist/server/render-tasks.d.ts +14 -2
  103. package/dist/server/render-tasks.js +38 -20
  104. package/dist/server/route-table.js +4 -1
  105. package/dist/server/settings.js +2 -1
  106. package/dist/server/task-views.js +10 -0
  107. package/dist/server/tasks.js +63 -0
  108. package/dist/shared-deps.d.ts +9 -4
  109. package/dist/shared-deps.js +89 -13
  110. package/dist/slack-socket.d.ts +12 -0
  111. package/dist/slack-socket.js +29 -0
  112. package/dist/slack.d.ts +0 -7
  113. package/dist/slack.js +2 -24
  114. package/dist/staged-runtimes.d.ts +16 -4
  115. package/dist/staged-runtimes.js +160 -38
  116. package/dist/steer-live.d.ts +19 -0
  117. package/dist/steer-live.js +93 -0
  118. package/dist/steer-mailbox.d.ts +39 -0
  119. package/dist/steer-mailbox.js +207 -0
  120. package/dist/steer-status.d.ts +25 -0
  121. package/dist/steer-status.js +44 -0
  122. package/dist/storage-sweep-child.d.ts +1 -0
  123. package/dist/storage-sweep-child.js +76 -0
  124. package/dist/storage-sweep-process.d.ts +41 -0
  125. package/dist/storage-sweep-process.js +187 -0
  126. package/dist/storage-sweep.d.ts +30 -6
  127. package/dist/storage-sweep.js +214 -28
  128. package/dist/storage-ui.d.ts +2 -0
  129. package/dist/storage-ui.js +16 -4
  130. package/dist/storage.d.ts +6 -0
  131. package/dist/storage.js +35 -0
  132. package/dist/store.d.ts +202 -2
  133. package/dist/store.js +440 -13
  134. package/dist/surface.js +5 -3
  135. package/dist/sweep-gate.d.ts +10 -0
  136. package/dist/sweep-gate.js +18 -0
  137. package/dist/sweep-quarantine.d.ts +28 -0
  138. package/dist/sweep-quarantine.js +153 -0
  139. package/dist/task-status.d.ts +2 -0
  140. package/dist/task-status.js +5 -1
  141. package/dist/team-updates.js +3 -3
  142. package/dist/test-temp.d.ts +6 -3
  143. package/dist/test-temp.js +15 -9
  144. package/dist/toolroll-update.js +15 -2
  145. package/dist/work-index.d.ts +1 -0
  146. package/dist/work-index.js +32 -12
  147. package/dist/work-summary.d.ts +2 -2
  148. package/dist/workspace-revision.js +3 -1
  149. package/dist/worktree.d.ts +3 -1
  150. package/dist/worktree.js +93 -10
  151. package/package.json +21 -21
  152. package/scripts/provider-canary.mjs +235 -147
@@ -0,0 +1,234 @@
1
+ /* @generated by youi from the Signal design language v1.0.0. Do not edit: change the tokens in design-language and run `youi sync`. */
2
+ /* Built with Terrazzo (DTCG 2025.10 resolver). */
3
+ :root {
4
+ color-scheme: light;
5
+ --so-accent: #171717;
6
+ --so-accent-hover: #383838;
7
+ --so-accent-text: #171717;
8
+ --so-attention: var(--so-signal);
9
+ --so-attention-soft: var(--so-signal-soft);
10
+ --so-code-bg: #fafafa;
11
+ /* Vermilion: failure and irreversible acts. */
12
+ --so-danger: #c4320a;
13
+ --so-danger-soft: #feebe7;
14
+ --so-ease-out: cubic-bezier(0.23, 1, 0.32, 1);
15
+ --so-font-mono: "Geist Mono", ui-monospace, "SF Mono", "SFMono-Regular", "Menlo", "Consolas", monospace;
16
+ --so-font-sans: "Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
17
+ /* Frame Grey: the frame the sidebar sits on, and the page background of server pages. */
18
+ --so-ground: #efefef;
19
+ /* Live Blue: something is running right now. */
20
+ --so-info: #0d74ce;
21
+ --so-info-soft: #e6f4fe;
22
+ /* Ink: all body text, and the colour of every act: primary buttons, the New task button, switches and radio cards when on. */
23
+ --so-ink: #171717;
24
+ /* Control Line: the boundary of fields, the composer, and the switch track when off. */
25
+ --so-input-line: #d4d4d4;
26
+ /* Hairline: every divider. */
27
+ --so-line: #e6e6e6;
28
+ --so-live: #0d74ce;
29
+ /* Muted: meta lines, fact keys, ghost buttons, placeholders, idle tab counts. */
30
+ --so-muted: #666666;
31
+ --so-nav-current: #ffffff;
32
+ --so-nav-current-ink: #171717;
33
+ --so-nav-hover: #e4e4e4;
34
+ /* Nav Ink: idle sidebar rows and their hover fill on the frame. */
35
+ --so-nav-ink: #525252;
36
+ --so-neutral-ink: #525252;
37
+ /* Neutral Badge: the badge for anything that is a fact and not a claim on the person, such as queued, running or built today in a project peek, or a count in a fold. */
38
+ --so-neutral-soft: #f0f0f0;
39
+ --so-on-accent: #ffffff;
40
+ --so-on-attention: var(--so-on-signal);
41
+ --so-on-signal: #ffffff;
42
+ --so-overlay: rgb(0 0 0 / .32);
43
+ /* Paper: every sheet, card, field, menu and dialog. */
44
+ --so-paper: #ffffff;
45
+ --so-phone-block: 14px;
46
+ --so-phone-card: 12px;
47
+ --so-phone-gap: 12px;
48
+ --so-phone-gutter: 16px;
49
+ --so-phone-row: 8px;
50
+ --so-pill-shadow: 0px 0px 0px 1px rgb(0 0 0 / .06), 0px 1px 2px 0px rgb(0 0 0 / .06);
51
+ --so-radius-badge: 5px;
52
+ --so-radius-card: 10px;
53
+ --so-radius-full: 9999px;
54
+ --so-radius-md: 8px;
55
+ --so-radius-sheet: 12px;
56
+ --so-radius-sm: 6px;
57
+ /* Raised: segmented-tab tracks, notices, row hover fills, and the muted well behind a "next step" block or a neutral problem line. */
58
+ --so-raised: #f5f5f5;
59
+ --so-scroll: #cfcfcf;
60
+ /* Selection: text selection highlight, with ink text on it. */
61
+ --so-selection: #d4d4d4;
62
+ --so-shadow-overlay: 0px 0px 0px 1px rgb(0 0 0 / .08), 0px 24px 48px -12px rgb(0 0 0 / .22);
63
+ --so-sheet-shadow: 0px 0px 0px 1px rgb(0 0 0 / .06), 0px 1px 2px 0px rgb(0 0 0 / .04), 0px 4px 12px -6px rgb(0 0 0 / .06);
64
+ --so-sidebar: #efefef;
65
+ /* Ink accent: the default accent, used for what waits on a person. */
66
+ --so-signal: #171717;
67
+ --so-signal-hover: #383838;
68
+ /* Ink wash: the fill behind attention badge text and the 3px focus halo around a focused field or composer — one step stronger than the neutral badge fill, so a waiting badge still reads first. */
69
+ --so-signal-soft: #e8e8e8;
70
+ /* Soft: hover fills on outline and ghost buttons, menu items and suggestions. */
71
+ --so-soft: #f2f2f2;
72
+ --so-space-2xl: 24px;
73
+ --so-space-3xl: 28px;
74
+ --so-space-lg: 16px;
75
+ --so-space-md: 12px;
76
+ --so-space-sm: 8px;
77
+ --so-space-xl: 20px;
78
+ --so-space-xs: 4px;
79
+ /* Built Green: done and passed, used for "Checks passed", "Open now" and a finished live step. */
80
+ --so-success: #218358;
81
+ --so-success-soft: #e6f6eb;
82
+ --so-typography-badge: var(--so-typography-badge-font-weight) var(--so-typography-badge-font-size)/var(--so-typography-badge-line-height) var(--so-typography-badge-font-family);
83
+ --so-typography-badge-font-family: var(--so-font-sans);
84
+ --so-typography-badge-font-size: 11.5px;
85
+ --so-typography-badge-font-weight: 500;
86
+ --so-typography-badge-line-height: 1.5652;
87
+ --so-typography-body: var(--so-typography-body-font-weight) var(--so-typography-body-font-size)/var(--so-typography-body-line-height) var(--so-typography-body-font-family);
88
+ --so-typography-body-font-family: var(--so-font-sans);
89
+ --so-typography-body-font-size: 13px;
90
+ --so-typography-body-font-weight: 400;
91
+ --so-typography-body-line-height: 1.5;
92
+ --so-typography-body-reading: var(--so-typography-body-reading-font-weight) var(--so-typography-body-reading-font-size)/var(--so-typography-body-reading-line-height) var(--so-typography-body-reading-font-family);
93
+ --so-typography-body-reading-font-family: var(--so-font-sans);
94
+ --so-typography-body-reading-font-size: 14px;
95
+ --so-typography-body-reading-font-weight: 400;
96
+ --so-typography-body-reading-line-height: 1.7;
97
+ --so-typography-caption: var(--so-typography-caption-font-weight) var(--so-typography-caption-font-size)/var(--so-typography-caption-line-height) var(--so-typography-caption-font-family);
98
+ --so-typography-caption-font-family: var(--so-font-sans);
99
+ --so-typography-caption-font-size: 12px;
100
+ --so-typography-caption-font-weight: 500;
101
+ --so-typography-caption-line-height: 1.5;
102
+ --so-typography-display: var(--so-typography-display-font-weight) var(--so-typography-display-font-size)/var(--so-typography-display-line-height) var(--so-typography-display-font-family);
103
+ --so-typography-display-font-family: var(--so-font-sans);
104
+ --so-typography-display-font-size: 26px;
105
+ --so-typography-display-font-weight: 600;
106
+ --so-typography-display-letter-spacing: -0.65px;
107
+ --so-typography-display-line-height: 1.25;
108
+ --so-typography-headline: var(--so-typography-headline-font-weight) var(--so-typography-headline-font-size)/var(--so-typography-headline-line-height) var(--so-typography-headline-font-family);
109
+ --so-typography-headline-font-family: var(--so-font-sans);
110
+ --so-typography-headline-font-size: 18px;
111
+ --so-typography-headline-font-weight: 600;
112
+ --so-typography-headline-line-height: 1.375;
113
+ --so-typography-label: var(--so-typography-label-font-weight) var(--so-typography-label-font-size)/var(--so-typography-label-line-height) var(--so-typography-label-font-family);
114
+ --so-typography-label-font-family: var(--so-font-sans);
115
+ --so-typography-label-font-size: 13px;
116
+ --so-typography-label-font-weight: 500;
117
+ --so-typography-label-line-height: 1.2;
118
+ --so-typography-mono: var(--so-typography-mono-font-weight) var(--so-typography-mono-font-size)/var(--so-typography-mono-line-height) var(--so-typography-mono-font-family);
119
+ --so-typography-mono-count: var(--so-typography-mono-count-font-weight) var(--so-typography-mono-count-font-size)/var(--so-typography-mono-count-line-height) var(--so-typography-mono-count-font-family);
120
+ --so-typography-mono-count-font-family: var(--so-font-mono);
121
+ --so-typography-mono-count-font-size: 11px;
122
+ --so-typography-mono-count-font-weight: 500;
123
+ --so-typography-mono-count-line-height: 1;
124
+ --so-typography-mono-font-family: var(--so-font-mono);
125
+ --so-typography-mono-font-size: 12px;
126
+ --so-typography-mono-font-weight: 400;
127
+ --so-typography-mono-line-height: 1.5;
128
+ --so-typography-title: var(--so-typography-title-font-weight) var(--so-typography-title-font-size)/var(--so-typography-title-line-height) var(--so-typography-title-font-family);
129
+ --so-typography-title-font-family: var(--so-font-sans);
130
+ --so-typography-title-font-size: 15px;
131
+ --so-typography-title-font-weight: 600;
132
+ --so-typography-title-letter-spacing: -0.15px;
133
+ --so-typography-title-line-height: 1.4;
134
+ --so-typography-title-sm: var(--so-typography-title-sm-font-weight) var(--so-typography-title-sm-font-size)/var(--so-typography-title-sm-line-height) var(--so-typography-title-sm-font-family);
135
+ --so-typography-title-sm-font-family: var(--so-font-sans);
136
+ --so-typography-title-sm-font-size: 14px;
137
+ --so-typography-title-sm-font-weight: 600;
138
+ --so-typography-title-sm-letter-spacing: -0.14px;
139
+ --so-typography-title-sm-line-height: 1.3;
140
+ --so-user-bubble: #f2f2f2;
141
+ /* Amber: setup trouble and caution. */
142
+ --so-warning: #ab6400;
143
+ --so-warning-soft: #fff4d5;
144
+ }
145
+
146
+ @media (prefers-color-scheme: dark) {
147
+ :root:not([data-theme="light"]) {
148
+ color-scheme: dark;
149
+ --so-accent: #ededed;
150
+ --so-accent-hover: #ffffff;
151
+ --so-accent-text: #ededed;
152
+ --so-code-bg: #111111;
153
+ --so-danger: #ff977d;
154
+ --so-danger-soft: rgb(255 151 125 / .12);
155
+ --so-ground: #0b0b0b;
156
+ --so-info: #70b8ff;
157
+ --so-info-soft: rgb(112 184 255 / .12);
158
+ --so-ink: #ededed;
159
+ --so-input-line: #363636;
160
+ --so-line: #262626;
161
+ --so-live: #70b8ff;
162
+ --so-muted: #a1a1a1;
163
+ --so-nav-current: #1f1f1f;
164
+ --so-nav-current-ink: #ededed;
165
+ --so-nav-hover: #171717;
166
+ --so-nav-ink: #a1a1a1;
167
+ --so-neutral-ink: #b4b4b4;
168
+ --so-neutral-soft: #1f1f1f;
169
+ --so-on-accent: #0a0a0a;
170
+ --so-on-signal: #0a0a0a;
171
+ --so-overlay: rgb(0 0 0 / .6);
172
+ --so-paper: #161616;
173
+ --so-pill-shadow: 0px 0px 0px 1px #2a2a2a, 0px 1px 2px 0px rgb(0 0 0 / .5);
174
+ --so-raised: #1c1c1c;
175
+ --so-scroll: #333333;
176
+ --so-selection: #3a3a3a;
177
+ --so-shadow-overlay: 0px 0px 0px 1px #2e2e2e, 0px 24px 48px -12px rgb(0 0 0 / .7);
178
+ --so-sheet-shadow: 0px 0px 0px 1px #262626, 0px 1px 2px 0px rgb(0 0 0 / .4);
179
+ --so-sidebar: #0b0b0b;
180
+ --so-signal: #ededed;
181
+ --so-signal-hover: #ffffff;
182
+ --so-signal-soft: #2e2e2e;
183
+ --so-soft: #1f1f1f;
184
+ --so-success: #3dd68c;
185
+ --so-success-soft: rgb(61 214 140 / .12);
186
+ --so-user-bubble: #202020;
187
+ --so-warning: #ffca16;
188
+ --so-warning-soft: rgb(255 202 22 / .12);
189
+ }
190
+ }
191
+
192
+ :root[data-theme="dark"] {
193
+ color-scheme: dark;
194
+ --so-accent: #ededed;
195
+ --so-accent-hover: #ffffff;
196
+ --so-accent-text: #ededed;
197
+ --so-code-bg: #111111;
198
+ --so-danger: #ff977d;
199
+ --so-danger-soft: rgb(255 151 125 / .12);
200
+ --so-ground: #0b0b0b;
201
+ --so-info: #70b8ff;
202
+ --so-info-soft: rgb(112 184 255 / .12);
203
+ --so-ink: #ededed;
204
+ --so-input-line: #363636;
205
+ --so-line: #262626;
206
+ --so-live: #70b8ff;
207
+ --so-muted: #a1a1a1;
208
+ --so-nav-current: #1f1f1f;
209
+ --so-nav-current-ink: #ededed;
210
+ --so-nav-hover: #171717;
211
+ --so-nav-ink: #a1a1a1;
212
+ --so-neutral-ink: #b4b4b4;
213
+ --so-neutral-soft: #1f1f1f;
214
+ --so-on-accent: #0a0a0a;
215
+ --so-on-signal: #0a0a0a;
216
+ --so-overlay: rgb(0 0 0 / .6);
217
+ --so-paper: #161616;
218
+ --so-pill-shadow: 0px 0px 0px 1px #2a2a2a, 0px 1px 2px 0px rgb(0 0 0 / .5);
219
+ --so-raised: #1c1c1c;
220
+ --so-scroll: #333333;
221
+ --so-selection: #3a3a3a;
222
+ --so-shadow-overlay: 0px 0px 0px 1px #2e2e2e, 0px 24px 48px -12px rgb(0 0 0 / .7);
223
+ --so-sheet-shadow: 0px 0px 0px 1px #262626, 0px 1px 2px 0px rgb(0 0 0 / .4);
224
+ --so-sidebar: #0b0b0b;
225
+ --so-signal: #ededed;
226
+ --so-signal-hover: #ffffff;
227
+ --so-signal-soft: #2e2e2e;
228
+ --so-soft: #1f1f1f;
229
+ --so-success: #3dd68c;
230
+ --so-success-soft: rgb(61 214 140 / .12);
231
+ --so-user-bubble: #202020;
232
+ --so-warning: #ffca16;
233
+ --so-warning-soft: rgb(255 202 22 / .12);
234
+ }
@@ -94,7 +94,6 @@ export declare function prepareDesktopUpdate(stateDir: string, installed: string
94
94
  export declare function sqliteLock(file: string, busyTimeoutMs?: number): DatabaseSync | null;
95
95
  export declare const installationLock: (j: UpdateJournal) => DatabaseSync | null;
96
96
  export declare function desktopUpdateStatus(stateDir: string): {
97
- version?: never;
98
97
  active: boolean;
99
98
  phase: string;
100
99
  detail: string;
@@ -108,6 +107,7 @@ export declare function desktopUpdateStatus(stateDir: string): {
108
107
  wasRunning?: never;
109
108
  backupPath?: never;
110
109
  updateId?: never;
110
+ version?: never;
111
111
  buildId?: never;
112
112
  workDir?: never;
113
113
  } | {
package/dist/discord.js CHANGED
@@ -1,7 +1,6 @@
1
1
  /** A single leased Discord Gateway connection, hosted by the ordinary worker. */
2
2
  import { randomBytes } from "node:crypto";
3
3
  import { setTimeout as sleep } from "node:timers/promises";
4
- import { Client, GatewayIntentBits, Partials } from "discord.js";
5
4
  import { loadDiscordCredentials, discordApi, object, discordId, } from "./discord-api.js";
6
5
  import { ChatState, chatHash } from "./chat-delivery-state.js";
7
6
  import { receiveDiscord, processDiscordEvent, deliverDiscordPart, planDiscordNotifications, planDiscordRooms, } from "./discord-chat.js";
@@ -39,6 +38,8 @@ export async function followDiscord(options) {
39
38
  };
40
39
  const problem = (message) => state.setProblem(credentials.installation, owner, message);
41
40
  const api = discordApi(credentials.botToken);
41
+ // The SDK loads once Discord is set up, not with every command that could host the follower.
42
+ const { Client, GatewayIntentBits, Partials } = await import("discord.js");
42
43
  const client = new Client({
43
44
  // Guild messages and their content are needed for rooms; the Message
44
45
  // Content intent must be enabled for the app in Discord's developer portal.
@@ -84,7 +84,7 @@ export function evidencePack(store, taskId, access, who, now, evidenceRoot, chec
84
84
  result: receipt === null ? null : {
85
85
  runId: receipt.runId, base: receipt.base, head: receipt.head, kind: receipt.completionKind,
86
86
  checks: { status: receipt.checks.status, command: receipt.checks.command, exitCode: receipt.checks.exitCode, detail: receipt.checks.detail },
87
- completedBy: assignment?.completion?.actor ?? null, completedAt: assignment?.completion?.at ?? null, caveats: receipt.caveats,
87
+ completedBy: assignment?.completion?.landed !== undefined ? `Toolroll, automatically: ${assignment.completion.landed}` : assignment?.completion?.actor ?? null, completedAt: assignment?.completion?.at ?? null, caveats: receipt.caveats,
88
88
  publication: assignment?.publication ?? null,
89
89
  },
90
90
  totals: { runs: allRuns.length, costUsd: Math.round(sum(run => run.costUsd) * 1e6) / 1e6, tokensIn: sum(run => run.tokensIn), tokensOut: sum(run => run.tokensOut) },
@@ -47,6 +47,13 @@ export declare const PROGRESS_PREFIX = "STANDING-ORDERS-PROGRESS-";
47
47
  * (adaptive execution plans): a terminal file like park or proof, written
48
48
  * at most once, read once at settlement. */
49
49
  export declare const PROPOSAL_PREFIX = "STANDING-ORDERS-PROPOSAL-";
50
+ /** The running build's steering mailbox: a FOLDER, not a file. The pulse
51
+ * writes each operator note into it as NNN.json (a temp name, then a
52
+ * rename); the agent acknowledges a note by moving it into `handled/`. */
53
+ export declare const STEER_PREFIX = "STANDING-ORDERS-STEER-";
54
+ /** The running build's status file (Agents board): JSON lines the agent appends
55
+ * (working, needs-decision, blocked, done), read forward by the pulse. */
56
+ export declare const STATUS_PREFIX = "STANDING-ORDERS-STATUS-";
50
57
  export declare const MAILBOX_SUFFIX = ".json";
51
58
  /** v105: left in a run's evidence folder when the retention setting removed its files (retention.ts). */
52
59
  export declare const RETENTION_NOTE = "REMOVED-BY-RETENTION";
@@ -78,6 +85,14 @@ export declare function looksLikeMailbox(name: string): boolean;
78
85
  * The pre-rename NIGHTORDERS- prefix stays recognized here — a worktree cut
79
86
  * down before 2026-08-13 may still hold one, and cleanup has no vintage. */
80
87
  export declare function looksLikeProtocolFile(name: string): boolean;
88
+ /** A steering mailbox folder, or anything in it, as a directory listing or
89
+ * `git status` names it: an untracked folder is reported with a trailing
90
+ * slash, or file by file where a repository shows every untracked file. */
91
+ export declare function looksLikeSteerFolder(name: string): boolean;
92
+ /** An agent status file (Agents board), by its exact nonce-shaped name. */
93
+ export declare function looksLikeStatusFile(name: string): boolean;
94
+ export declare function statusFileName(): string;
95
+ export declare function steerFolderName(): string;
81
96
  export declare function handoffName(): string;
82
97
  export declare function planFileName(): string;
83
98
  /** The build's optional proof manifest (Priority 2), named exactly like
@@ -208,6 +223,19 @@ export declare function readVerifiedArtifact(root: string, artifact: Artifact):
208
223
  ok: false;
209
224
  problem: string;
210
225
  };
226
+ /**
227
+ * Whether an artifact's bytes still verify, without returning them. Status
228
+ * sweeps ask this of every artifact of every run, so a file proved earlier and
229
+ * not touched since (same artifact row, device, inode, size, mtime and ctime
230
+ * on the descriptor just opened) costs an fstat, not a re-read and SHA-256.
231
+ * Every path check still runs on each call; a changed or new file is hashed.
232
+ */
233
+ export declare function verifyArtifact(root: string, artifact: Artifact): {
234
+ ok: true;
235
+ } | {
236
+ ok: false;
237
+ problem: string;
238
+ };
211
239
  /** The file alone, no row — quarantines use this; they belong to no live run. */
212
240
  export declare function writeEvidenceFile(root: string, runId: number, name: string, content: Buffer): string;
213
241
  /**
package/dist/evidence.js CHANGED
@@ -14,7 +14,7 @@
14
14
  * store holds keys relative to that root, never absolute paths.
15
15
  */
16
16
  import { createHash, randomBytes } from "node:crypto";
17
- import { closeSync, existsSync, constants, fstatSync, mkdirSync, openSync, readdirSync, readSync, realpathSync, unlinkSync, writeFileSync, } from "node:fs";
17
+ import { closeSync, existsSync, constants, fstatSync, mkdirSync, openSync, readdirSync, readSync, realpathSync, rmSync, unlinkSync, writeFileSync, } from "node:fs";
18
18
  import { join, sep } from "node:path";
19
19
  import { namedPath } from "./names.js";
20
20
  import { LIMITS } from "./decision.js";
@@ -50,6 +50,13 @@ export const PROGRESS_PREFIX = "STANDING-ORDERS-PROGRESS-";
50
50
  * (adaptive execution plans): a terminal file like park or proof, written
51
51
  * at most once, read once at settlement. */
52
52
  export const PROPOSAL_PREFIX = "STANDING-ORDERS-PROPOSAL-";
53
+ /** The running build's steering mailbox: a FOLDER, not a file. The pulse
54
+ * writes each operator note into it as NNN.json (a temp name, then a
55
+ * rename); the agent acknowledges a note by moving it into `handled/`. */
56
+ export const STEER_PREFIX = "STANDING-ORDERS-STEER-";
57
+ /** The running build's status file (Agents board): JSON lines the agent appends
58
+ * (working, needs-decision, blocked, done), read forward by the pulse. */
59
+ export const STATUS_PREFIX = "STANDING-ORDERS-STATUS-";
53
60
  export const MAILBOX_SUFFIX = ".json";
54
61
  /** v105: left in a run's evidence folder when the retention setting removed its files (retention.ts). */
55
62
  export const RETENTION_NOTE = "REMOVED-BY-RETENTION";
@@ -128,6 +135,8 @@ export function looksLikeMailbox(name) {
128
135
  * The pre-rename NIGHTORDERS- prefix stays recognized here — a worktree cut
129
136
  * down before 2026-08-13 may still hold one, and cleanup has no vintage. */
130
137
  export function looksLikeProtocolFile(name) {
138
+ if (looksLikeSteerFolder(name) || looksLikeStatusFile(name))
139
+ return true;
131
140
  return ((name.startsWith(MAILBOX_PREFIX) ||
132
141
  name.startsWith(HANDOFF_PREFIX) ||
133
142
  name.startsWith(PLAN_PREFIX) ||
@@ -141,6 +150,22 @@ export function looksLikeProtocolFile(name) {
141
150
  name.startsWith("NIGHTORDERS-")) &&
142
151
  name.endsWith(MAILBOX_SUFFIX));
143
152
  }
153
+ /** A steering mailbox folder, or anything in it, as a directory listing or
154
+ * `git status` names it: an untracked folder is reported with a trailing
155
+ * slash, or file by file where a repository shows every untracked file. */
156
+ export function looksLikeSteerFolder(name) {
157
+ return /^STANDING-ORDERS-STEER-[0-9a-f]{16}(?:\/.*)?$/.test(name);
158
+ }
159
+ /** An agent status file (Agents board), by its exact nonce-shaped name. */
160
+ export function looksLikeStatusFile(name) {
161
+ return /^STANDING-ORDERS-STATUS-[0-9a-f]{16}\.jsonl$/.test(name);
162
+ }
163
+ export function statusFileName() {
164
+ return `${STATUS_PREFIX}${randomBytes(8).toString("hex")}.jsonl`;
165
+ }
166
+ export function steerFolderName() {
167
+ return `${STEER_PREFIX}${randomBytes(8).toString("hex")}`;
168
+ }
144
169
  export function handoffName() {
145
170
  return `${HANDOFF_PREFIX}${randomBytes(8).toString("hex")}${MAILBOX_SUFFIX}`;
146
171
  }
@@ -412,6 +437,29 @@ export function storeEvidence(store, root, runId, kind, name, content, capture,
412
437
  // Recovery uses this read boundary for retained receipts and handoffs: inspecting
413
438
  // bytes must never reseal a missing artifact or turn a saved failure into a pass.
414
439
  export function readVerifiedArtifact(root, artifact) {
440
+ return withArtifactFile(root, artifact, readAndHash);
441
+ }
442
+ /**
443
+ * Whether an artifact's bytes still verify, without returning them. Status
444
+ * sweeps ask this of every artifact of every run, so a file proved earlier and
445
+ * not touched since (same artifact row, device, inode, size, mtime and ctime
446
+ * on the descriptor just opened) costs an fstat, not a re-read and SHA-256.
447
+ * Every path check still runs on each call; a changed or new file is hashed.
448
+ */
449
+ export function verifyArtifact(root, artifact) {
450
+ return withArtifactFile(root, artifact, (fd, stat, memoKey) => {
451
+ if (VERIFIED_FILES.get(memoKey) === fileIdentity(artifact, stat))
452
+ return { ok: true };
453
+ const read = readAndHash(fd, stat, memoKey, artifact);
454
+ return read.ok ? { ok: true } : read;
455
+ });
456
+ }
457
+ /** Files proved by an earlier read, keyed by artifact and resolved path. Bounded; the oldest proof is forgotten first. */
458
+ const VERIFIED_FILES = new Map();
459
+ const VERIFIED_FILES_LIMIT = 20_000;
460
+ const fileIdentity = (artifact, stat) => [artifact.sha256, artifact.bytesStored, stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].join(":");
461
+ /** The checks both readers share, on the descriptor `use` then reads. */
462
+ function withArtifactFile(root, artifact, use) {
415
463
  const segments = artifact.key.split("/");
416
464
  const wellFormed = segments.length > 0 &&
417
465
  segments.every(segment => segment.length > 0 &&
@@ -447,32 +495,40 @@ export function readVerifiedArtifact(root, artifact) {
447
495
  return { ok: false, problem: "cannot open the file without following a link" };
448
496
  }
449
497
  try {
450
- const stat = fstatSync(fd);
498
+ const stat = fstatSync(fd, { bigint: true });
451
499
  if (!stat.isFile())
452
500
  return { ok: false, problem: "not a regular file" };
453
501
  if (Number(stat.size) !== artifact.bytesStored) {
454
502
  return { ok: false, problem: "the file's size no longer matches its record" };
455
503
  }
456
- const buffer = Buffer.alloc(artifact.bytesStored);
457
- let offset = 0;
458
- while (offset < buffer.length) {
459
- const read = readSync(fd, buffer, offset, buffer.length - offset, offset);
460
- if (read <= 0)
461
- break;
462
- offset += read;
463
- }
464
- if (offset !== buffer.length)
465
- return { ok: false, problem: "the file ended early" };
466
- const digest = createHash("sha256").update(buffer).digest("hex");
467
- if (digest !== artifact.sha256) {
468
- return { ok: false, problem: "the file no longer hashes to its record" };
469
- }
470
- return { ok: true, content: buffer };
504
+ return use(fd, stat, `${artifact.id}\0${resolved}`, artifact);
471
505
  }
472
506
  finally {
473
507
  closeSync(fd);
474
508
  }
475
509
  }
510
+ /** Read the whole file from the open descriptor; only bytes matching the row's SHA-256 leave, and only they are remembered as proved. */
511
+ function readAndHash(fd, stat, memoKey, artifact) {
512
+ VERIFIED_FILES.delete(memoKey);
513
+ const buffer = Buffer.alloc(artifact.bytesStored);
514
+ let offset = 0;
515
+ while (offset < buffer.length) {
516
+ const read = readSync(fd, buffer, offset, buffer.length - offset, offset);
517
+ if (read <= 0)
518
+ break;
519
+ offset += read;
520
+ }
521
+ if (offset !== buffer.length)
522
+ return { ok: false, problem: "the file ended early" };
523
+ const digest = createHash("sha256").update(buffer).digest("hex");
524
+ if (digest !== artifact.sha256) {
525
+ return { ok: false, problem: "the file no longer hashes to its record" };
526
+ }
527
+ if (VERIFIED_FILES.size >= VERIFIED_FILES_LIMIT)
528
+ VERIFIED_FILES.delete(VERIFIED_FILES.keys().next().value);
529
+ VERIFIED_FILES.set(memoKey, fileIdentity(artifact, stat));
530
+ return { ok: true, content: buffer };
531
+ }
476
532
  /** The file alone, no row — quarantines use this; they belong to no live run. */
477
533
  export function writeEvidenceFile(root, runId, name, content) {
478
534
  const dir = join(root, String(runId));
@@ -502,6 +558,20 @@ export function quarantineMailboxes(worktree, root, runId) {
502
558
  if (!looksLikeProtocolFile(name))
503
559
  continue;
504
560
  const path = join(worktree, name);
561
+ // An earlier attempt's steering folder holds copies of notes the
562
+ // database already keeps: removed whole (a symlink is removed, never
563
+ // followed), never quarantined. So is its status file: the database
564
+ // already read every line it will ever need.
565
+ if (looksLikeSteerFolder(name) || looksLikeStatusFile(name)) {
566
+ try {
567
+ rmSync(path, { recursive: true, force: true });
568
+ swept.push(name);
569
+ }
570
+ catch {
571
+ // Its name carries an old nonce; nothing reads it again.
572
+ }
573
+ continue;
574
+ }
505
575
  if (root !== null && runId !== null) {
506
576
  const read = readMailbox(path);
507
577
  if (read.ok) {
package/dist/exec.d.ts CHANGED
@@ -9,6 +9,7 @@
9
9
  import { type ProcessTreeObserver } from "./process-tree.js";
10
10
  import { type ContainmentBackendId } from "./containment.js";
11
11
  import type { Store } from "./store.js";
12
+ import type { LiveInput } from "./live-input.js";
12
13
  export type ExecResult = {
13
14
  /** Process exit code, or one of the synthetic codes below. */
14
15
  code: number;
@@ -112,6 +113,15 @@ export type RunOptions = ProcessTreeObserver & {
112
113
  * currently only the streaming transport can deliver this early).
113
114
  */
114
115
  onSessionId?: (id: string) => void;
116
+ /**
117
+ * Live input (live-input.ts): a Claude transport that declares `streamInput`
118
+ * keeps stdin piped and open, hands it to this session, and shows it every
119
+ * stdout event. The session sends the brief first and decides when stdin
120
+ * closes; the run then ends with the process, and the LAST primary result
121
+ * is the accounting envelope (one result per turn), with every main
122
+ * result's usage and turns added up (cumulativeLiveResult).
123
+ */
124
+ liveInput?: LiveInput;
115
125
  /** Fires once the child exists, with its pid (the process-group id when
116
126
  * processGroup is set) — the slot ledger records it (v14 finding 26). */
117
127
  onSpawn?: (pid: number) => void;
@@ -202,6 +212,17 @@ export declare function run(file: string, args: readonly string[], options?: Run
202
212
  * worst useful case while remaining a hard, per-event memory boundary.
203
213
  */
204
214
  export declare const JSONL_EVENT_HARD_CAP: number;
215
+ /**
216
+ * The accounting an oversized Claude result still carries, read by a bounded scan of its kept prefix and tail: the
217
+ * session's running `total_cost_usd`, its `usage` and `num_turns`, and whether it is a main result at all (an
218
+ * `origin` naming anything but a person makes it a background task's). Anything unreadable or seen twice is absent.
219
+ */
220
+ export declare function scanOversizedClaudeResult(prefix: Buffer, tail: Buffer | undefined): {
221
+ primary: boolean;
222
+ account: Record<string, unknown>;
223
+ };
224
+ /** Marks a Claude result whose session cost could not be read: the gateway charges the run's dollar cap instead. */
225
+ export declare const CLAUDE_COST_UNKNOWN = "standing_orders_cost_unknown";
205
226
  export declare function runStreamJsonl(file: string, args: readonly string[], options?: RunOptions): Promise<ExecResult>;
206
227
  /**
207
228
  * The streaming transport for gemini's stream-json dialect (Phase 3 D1/A7).
@@ -225,4 +246,15 @@ export declare function runStreamJsonl(file: string, args: readonly string[], op
225
246
  * semantics match `runStreamJsonl`.
226
247
  */
227
248
  export declare function runGeminiStreamJsonl(file: string, args: readonly string[], options?: RunOptions): Promise<ExecResult>;
249
+ /**
250
+ * A live session's account so far (one main result per turn, checked on Claude Code 2.1.290): the latest result's
251
+ * session, ending, words and cost — `total_cost_usd` is already the session's running total — with the usage token
252
+ * fields and `num_turns`, which count only their own message, added up across every main result. A field that is not
253
+ * a non-negative number adds nothing.
254
+ */
255
+ export declare function cumulativeLiveResult(previous: Record<string, unknown>, latest: Record<string, unknown>): Record<string, unknown>;
256
+ /** This transport can keep stdin open for a live session (RunOptions.liveInput). */
257
+ export declare const STREAM_INPUT: unique symbol;
258
+ /** Whether a runner declares it keeps stdin open for a live session; an injected runner opts in by carrying the mark. */
259
+ export declare const streamsInput: (runner: unknown) => boolean;
228
260
  export declare function runClaudeStreamJsonl(file: string, args: readonly string[], options?: RunOptions): Promise<ExecResult>;