toolroll 0.9.94 → 0.9.95

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 (191) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/dist/agent-fence.d.ts +6 -0
  3. package/dist/agent-fence.js +2 -2
  4. package/dist/assignment-status.d.ts +1 -1
  5. package/dist/assignment-ui.js +2 -0
  6. package/dist/attest.d.ts +3 -0
  7. package/dist/attest.js +10 -0
  8. package/dist/browser/terminal.js +1 -1
  9. package/dist/browser/workspace.css +1 -1
  10. package/dist/browser/workspace.js +24 -24
  11. package/dist/browser-crew.js +2 -0
  12. package/dist/browser-workspace.d.ts +5 -0
  13. package/dist/build-review.js +2 -0
  14. package/dist/builder.d.ts +7 -2
  15. package/dist/builder.js +183 -79
  16. package/dist/card-catalog.d.ts +209 -0
  17. package/dist/card-catalog.js +133 -0
  18. package/dist/card-cli.d.ts +43 -0
  19. package/dist/card-cli.js +44 -0
  20. package/dist/card-render.d.ts +26 -0
  21. package/dist/card-render.js +223 -0
  22. package/dist/card-sources.d.ts +68 -0
  23. package/dist/card-sources.js +234 -0
  24. package/dist/chat-actions.d.ts +95 -8
  25. package/dist/chat-actions.js +449 -45
  26. package/dist/chat-approval.d.ts +13 -0
  27. package/dist/chat-approval.js +39 -9
  28. package/dist/chat-card-part.d.ts +11 -0
  29. package/dist/chat-card-part.js +18 -0
  30. package/dist/chat-card.d.ts +46 -0
  31. package/dist/chat-card.js +99 -0
  32. package/dist/chat-channel.d.ts +2 -0
  33. package/dist/chat-channel.js +26 -14
  34. package/dist/chat-controls.d.ts +4 -0
  35. package/dist/chat-controls.js +4 -0
  36. package/dist/chat-decide.d.ts +3 -14
  37. package/dist/chat-decide.js +43 -54
  38. package/dist/chat-delivery.js +13 -4
  39. package/dist/chat-flow.d.ts +0 -27
  40. package/dist/chat-flow.js +31 -2
  41. package/dist/chat-quiet.js +1 -1
  42. package/dist/chat-task-actions.d.ts +3 -2
  43. package/dist/chat-task-actions.js +19 -5
  44. package/dist/chat-terms.d.ts +14 -2
  45. package/dist/chat-terms.js +35 -0
  46. package/dist/check-failure.d.ts +2 -0
  47. package/dist/check-failure.js +34 -2
  48. package/dist/check-repair.d.ts +47 -0
  49. package/dist/check-repair.js +240 -0
  50. package/dist/claim.d.ts +6 -0
  51. package/dist/claim.js +13 -2
  52. package/dist/cli-help.d.ts +1 -1
  53. package/dist/cli-help.js +2 -0
  54. package/dist/cli.js +4 -0
  55. package/dist/coding-provider.d.ts +1 -1
  56. package/dist/coding-provider.js +9 -5
  57. package/dist/coding-workspace.d.ts +1 -1
  58. package/dist/coding-workspace.js +4 -4
  59. package/dist/contracts/chat-actions.d.ts +83 -0
  60. package/dist/contracts/chat-actions.js +18 -0
  61. package/dist/contracts/chat-card.d.ts +91 -0
  62. package/dist/contracts/chat-card.js +54 -0
  63. package/dist/contracts/chat-content.d.ts +191 -0
  64. package/dist/contracts/chat-content.js +4 -0
  65. package/dist/contracts/console-api.d.ts +2 -0
  66. package/dist/contracts/console-api.js +2 -0
  67. package/dist/contracts/flow-propose.d.ts +8 -1
  68. package/dist/contracts/flow-propose.js +3 -1
  69. package/dist/contracts/flow-send.d.ts +126 -0
  70. package/dist/contracts/flow-send.js +3 -0
  71. package/dist/contracts/lead-tools.d.ts +173 -0
  72. package/dist/contracts/lead-tools.js +15 -0
  73. package/dist/contracts/store-json.d.ts +16 -0
  74. package/dist/discord-chat.js +14 -0
  75. package/dist/dispatch.d.ts +1 -1
  76. package/dist/dispatch.js +7 -1
  77. package/dist/dispose.d.ts +19 -0
  78. package/dist/dispose.js +47 -7
  79. package/dist/eval-oracle.js +4 -1
  80. package/dist/evidence-pair.d.ts +53 -0
  81. package/dist/evidence-pair.js +128 -0
  82. package/dist/exhaustion.d.ts +17 -4
  83. package/dist/exhaustion.js +44 -4
  84. package/dist/export.js +1 -1
  85. package/dist/failing-first.d.ts +31 -0
  86. package/dist/failing-first.js +44 -0
  87. package/dist/fallback-schema.d.ts +8 -0
  88. package/dist/fallback-schema.js +40 -0
  89. package/dist/fallback.d.ts +138 -0
  90. package/dist/fallback.js +605 -0
  91. package/dist/findings.d.ts +224 -0
  92. package/dist/findings.js +449 -0
  93. package/dist/fleet-schema.d.ts +17 -0
  94. package/dist/fleet-schema.js +135 -0
  95. package/dist/flow-browser.js +2 -2
  96. package/dist/flow-code.d.ts +2 -2
  97. package/dist/flow-code.js +9 -5
  98. package/dist/flow-result-card.d.ts +22 -0
  99. package/dist/flow-result-card.js +55 -0
  100. package/dist/flow-send.js +12 -9
  101. package/dist/flow-steps.js +1 -1
  102. package/dist/flow-triggers.js +1 -1
  103. package/dist/flow-unattended.d.ts +26 -0
  104. package/dist/flow-unattended.js +12 -3
  105. package/dist/flows-ui.js +10 -2
  106. package/dist/follow-through-brief.d.ts +12 -0
  107. package/dist/follow-through-brief.js +31 -0
  108. package/dist/follow-through-cli.d.ts +23 -0
  109. package/dist/follow-through-cli.js +153 -0
  110. package/dist/git-incident.d.ts +16 -0
  111. package/dist/git-incident.js +69 -6
  112. package/dist/handover-note.d.ts +33 -0
  113. package/dist/handover-note.js +106 -0
  114. package/dist/invoke.d.ts +20 -1
  115. package/dist/invoke.js +108 -44
  116. package/dist/lead-cli.js +26 -24
  117. package/dist/lead-contract.js +1 -0
  118. package/dist/lead-doors.d.ts +4 -0
  119. package/dist/lead-doors.js +2 -2
  120. package/dist/lead-progress.js +2 -0
  121. package/dist/lead-tools.js +111 -32
  122. package/dist/lead.js +1 -1
  123. package/dist/local-owner.js +5 -0
  124. package/dist/modes.d.ts +6 -6
  125. package/dist/modes.js +3 -1
  126. package/dist/needs-you.d.ts +5 -2
  127. package/dist/needs-you.js +14 -2
  128. package/dist/operate-remote-arguments.js +1 -0
  129. package/dist/operate-remote.js +1 -1
  130. package/dist/operate.d.ts +1 -1
  131. package/dist/operate.js +165 -14
  132. package/dist/plan-login.d.ts +14 -0
  133. package/dist/plan-login.js +58 -0
  134. package/dist/planner.d.ts +1 -1
  135. package/dist/planner.js +2 -1
  136. package/dist/project-delete-ui.d.ts +12 -0
  137. package/dist/project-delete-ui.js +18 -1
  138. package/dist/project-delete.js +6 -0
  139. package/dist/project-knowledge.d.ts +6 -6
  140. package/dist/project-tools.d.ts +4 -4
  141. package/dist/project-tools.js +23 -7
  142. package/dist/protected-storage-look.d.ts +1 -0
  143. package/dist/protected-storage-look.js +13 -0
  144. package/dist/protected-storage.d.ts +70 -39
  145. package/dist/protected-storage.js +196 -147
  146. package/dist/provider-capture.d.ts +34 -0
  147. package/dist/provider-capture.js +53 -0
  148. package/dist/release-carry.js +10 -1
  149. package/dist/release.js +9 -1
  150. package/dist/scope.d.ts +22 -3
  151. package/dist/scope.js +71 -20
  152. package/dist/scout.d.ts +1 -1
  153. package/dist/scout.js +2 -1
  154. package/dist/serve.js +2 -0
  155. package/dist/server/chat.js +2 -2
  156. package/dist/server/console.css +50 -0
  157. package/dist/server/render-chat.js +14 -1
  158. package/dist/server/render-follow-through.d.ts +11 -0
  159. package/dist/server/render-follow-through.js +68 -0
  160. package/dist/server/render-results.js +1 -1
  161. package/dist/server/render-tasks.d.ts +3 -0
  162. package/dist/server/render-tasks.js +24 -5
  163. package/dist/server/route-table.js +3 -0
  164. package/dist/server/settings.js +37 -12
  165. package/dist/server/task-views.js +5 -0
  166. package/dist/server/tasks.js +76 -0
  167. package/dist/settings-groups-ui.d.ts +1 -0
  168. package/dist/settings-groups-ui.js +3 -1
  169. package/dist/settings-kit.js +3 -2
  170. package/dist/settings-proofs.js +3 -1
  171. package/dist/setup-guide.d.ts +2 -2
  172. package/dist/slack-chat.js +14 -1
  173. package/dist/steer-mailbox.d.ts +8 -1
  174. package/dist/steer-mailbox.js +164 -6
  175. package/dist/store.d.ts +11 -4
  176. package/dist/store.js +93 -30
  177. package/dist/surface.js +24 -4
  178. package/dist/task-control.d.ts +2 -1
  179. package/dist/task-status.js +5 -0
  180. package/dist/teardown-git.d.ts +2 -0
  181. package/dist/teardown-git.js +42 -16
  182. package/dist/telegram-lead.js +23 -6
  183. package/dist/telegram.js +12 -1
  184. package/dist/toolroll-update.js +2 -0
  185. package/dist/work-index.js +22 -7
  186. package/dist/work-summary.d.ts +1 -1
  187. package/dist/work-summary.js +5 -2
  188. package/dist/workspace-ui.d.ts +3 -1
  189. package/dist/workspace-ui.js +10 -1
  190. package/dist/worktree.js +23 -1
  191. package/package.json +2 -1
@@ -92,6 +92,22 @@ export declare const LEAD_TOOL_INPUTS: {
92
92
  secrets: z.ZodOptional<z.ZodArray<z.ZodString>>;
93
93
  about: z.ZodOptional<z.ZodString>;
94
94
  on: z.ZodOptional<z.ZodBoolean>;
95
+ finding: z.ZodOptional<z.ZodInt>;
96
+ summary: z.ZodOptional<z.ZodString>;
97
+ file: z.ZodOptional<z.ZodString>;
98
+ line: z.ZodOptional<z.ZodInt>;
99
+ pointer: z.ZodOptional<z.ZodString>;
100
+ revision: z.ZodOptional<z.ZodInt>;
101
+ incident: z.ZodOptional<z.ZodInt>;
102
+ provider: z.ZodOptional<z.ZodString>;
103
+ runner: z.ZodOptional<z.ZodString>;
104
+ persona: z.ZodOptional<z.ZodString>;
105
+ proposal: z.ZodOptional<z.ZodInt>;
106
+ decision: z.ZodOptional<z.ZodEnum<{
107
+ accept: "accept";
108
+ reject: "reject";
109
+ }>>;
110
+ reason: z.ZodOptional<z.ZodString>;
95
111
  }, z.core.$strict>;
96
112
  readonly propose_task_action: z.ZodObject<{
97
113
  task: z.ZodString;
@@ -110,6 +126,63 @@ export declare const LEAD_TOOL_INPUTS: {
110
126
  repo: z.ZodOptional<z.ZodString>;
111
127
  run: z.ZodOptional<z.ZodInt>;
112
128
  }, z.core.$strict>;
129
+ readonly show_card: z.ZodObject<{
130
+ spec: z.ZodObject<{
131
+ root: z.ZodString;
132
+ elements: z.ZodObject<{}, z.core.$catchall<z.ZodObject<{
133
+ type: z.ZodEnum<{
134
+ [x: string]: string;
135
+ }>;
136
+ props: z.ZodObject<{
137
+ source: z.ZodOptional<z.ZodEnum<{
138
+ builders: "builders";
139
+ findings: "findings";
140
+ flow_zones: "flow_zones";
141
+ flows: "flows";
142
+ handover: "handover";
143
+ integrations: "integrations";
144
+ mode: "mode";
145
+ projects: "projects";
146
+ providers: "providers";
147
+ results: "results";
148
+ spend: "spend";
149
+ tasks: "tasks";
150
+ }>>;
151
+ chart: z.ZodOptional<z.ZodEnum<{
152
+ area: "area";
153
+ bar: "bar";
154
+ line: "line";
155
+ pie: "pie";
156
+ }>>;
157
+ columns: z.ZodOptional<z.ZodArray<z.ZodString>>;
158
+ facts: z.ZodOptional<z.ZodArray<z.ZodString>>;
159
+ repo: z.ZodOptional<z.ZodString>;
160
+ state: z.ZodOptional<z.ZodEnum<{
161
+ cancelled: "cancelled";
162
+ done: "done";
163
+ failed: "failed";
164
+ queued: "queued";
165
+ running: "running";
166
+ }>>;
167
+ limit: z.ZodOptional<z.ZodInt>;
168
+ flow: z.ZodOptional<z.ZodInt>;
169
+ days: z.ZodOptional<z.ZodInt>;
170
+ text: z.ZodOptional<z.ZodString>;
171
+ control: z.ZodOptional<z.ZodEnum<{
172
+ [x: string]: string;
173
+ }>>;
174
+ task: z.ZodOptional<z.ZodString>;
175
+ run: z.ZodOptional<z.ZodInt>;
176
+ proposal: z.ZodOptional<z.ZodInt>;
177
+ }, z.core.$strict>;
178
+ children: z.ZodArray<z.ZodString>;
179
+ }, z.core.$strict>>>;
180
+ }, z.core.$strict>;
181
+ }, z.core.$strict>;
182
+ readonly get_findings: z.ZodObject<{
183
+ task: z.ZodOptional<z.ZodString>;
184
+ repo: z.ZodOptional<z.ZodString>;
185
+ }, z.core.$strict>;
113
186
  readonly offer_approval: z.ZodObject<{
114
187
  task: z.ZodOptional<z.ZodString>;
115
188
  card: z.ZodOptional<z.ZodInt>;
@@ -174,7 +247,9 @@ export declare const LEAD_TOOL_INPUTS: {
174
247
  add_card: "add_card";
175
248
  add_trigger: "add_trigger";
176
249
  allow_browser: "allow_browser";
250
+ allow_unattended: "allow_unattended";
177
251
  approve: "approve";
252
+ archive: "archive";
178
253
  assign: "assign";
179
254
  cancel_card: "cancel_card";
180
255
  choose: "choose";
@@ -191,11 +266,16 @@ export declare const LEAD_TOOL_INPUTS: {
191
266
  send_back: "send_back";
192
267
  starter: "starter";
193
268
  turn_off_browser: "turn_off_browser";
269
+ turn_off_unattended: "turn_off_unattended";
194
270
  unfollow: "unfollow";
195
271
  }>;
196
272
  repo: z.ZodOptional<z.ZodString>;
197
273
  flow: z.ZodOptional<z.ZodInt>;
198
274
  card: z.ZodOptional<z.ZodInt>;
275
+ days: z.ZodOptional<z.ZodInt>;
276
+ budget: z.ZodOptional<z.ZodString>;
277
+ runs: z.ZodOptional<z.ZodString>;
278
+ include: z.ZodOptional<z.ZodArray<z.ZodInt>>;
199
279
  kit: z.ZodOptional<z.ZodEnum<{
200
280
  [x: string]: string;
201
281
  }>>;
@@ -850,6 +930,22 @@ export declare const LEAD_TOOL_READ_INPUTS: {
850
930
  secrets: z.ZodOptional<z.ZodArray<z.ZodString>>;
851
931
  about: z.ZodOptional<z.ZodString>;
852
932
  on: z.ZodOptional<z.ZodBoolean>;
933
+ finding: z.ZodOptional<z.ZodInt>;
934
+ summary: z.ZodOptional<z.ZodString>;
935
+ file: z.ZodOptional<z.ZodString>;
936
+ line: z.ZodOptional<z.ZodInt>;
937
+ pointer: z.ZodOptional<z.ZodString>;
938
+ revision: z.ZodOptional<z.ZodInt>;
939
+ incident: z.ZodOptional<z.ZodInt>;
940
+ provider: z.ZodOptional<z.ZodString>;
941
+ runner: z.ZodOptional<z.ZodString>;
942
+ persona: z.ZodOptional<z.ZodString>;
943
+ proposal: z.ZodOptional<z.ZodInt>;
944
+ decision: z.ZodOptional<z.ZodEnum<{
945
+ accept: "accept";
946
+ reject: "reject";
947
+ }>>;
948
+ reason: z.ZodOptional<z.ZodString>;
853
949
  }, z.core.$strict>;
854
950
  readonly propose_task_action: z.ZodObject<{
855
951
  task: z.ZodString;
@@ -868,6 +964,63 @@ export declare const LEAD_TOOL_READ_INPUTS: {
868
964
  repo: z.ZodOptional<z.ZodString>;
869
965
  run: z.ZodOptional<z.ZodInt>;
870
966
  }, z.core.$strict>;
967
+ readonly show_card: z.ZodObject<{
968
+ spec: z.ZodObject<{
969
+ root: z.ZodString;
970
+ elements: z.ZodObject<{}, z.core.$catchall<z.ZodObject<{
971
+ type: z.ZodEnum<{
972
+ [x: string]: string;
973
+ }>;
974
+ props: z.ZodObject<{
975
+ source: z.ZodOptional<z.ZodEnum<{
976
+ builders: "builders";
977
+ findings: "findings";
978
+ flow_zones: "flow_zones";
979
+ flows: "flows";
980
+ handover: "handover";
981
+ integrations: "integrations";
982
+ mode: "mode";
983
+ projects: "projects";
984
+ providers: "providers";
985
+ results: "results";
986
+ spend: "spend";
987
+ tasks: "tasks";
988
+ }>>;
989
+ chart: z.ZodOptional<z.ZodEnum<{
990
+ area: "area";
991
+ bar: "bar";
992
+ line: "line";
993
+ pie: "pie";
994
+ }>>;
995
+ columns: z.ZodOptional<z.ZodArray<z.ZodString>>;
996
+ facts: z.ZodOptional<z.ZodArray<z.ZodString>>;
997
+ repo: z.ZodOptional<z.ZodString>;
998
+ state: z.ZodOptional<z.ZodEnum<{
999
+ cancelled: "cancelled";
1000
+ done: "done";
1001
+ failed: "failed";
1002
+ queued: "queued";
1003
+ running: "running";
1004
+ }>>;
1005
+ limit: z.ZodOptional<z.ZodInt>;
1006
+ flow: z.ZodOptional<z.ZodInt>;
1007
+ days: z.ZodOptional<z.ZodInt>;
1008
+ text: z.ZodOptional<z.ZodString>;
1009
+ control: z.ZodOptional<z.ZodEnum<{
1010
+ [x: string]: string;
1011
+ }>>;
1012
+ task: z.ZodOptional<z.ZodString>;
1013
+ run: z.ZodOptional<z.ZodInt>;
1014
+ proposal: z.ZodOptional<z.ZodInt>;
1015
+ }, z.core.$strict>;
1016
+ children: z.ZodArray<z.ZodString>;
1017
+ }, z.core.$strict>>>;
1018
+ }, z.core.$strict>;
1019
+ }, z.core.$strict>;
1020
+ readonly get_findings: z.ZodObject<{
1021
+ task: z.ZodOptional<z.ZodString>;
1022
+ repo: z.ZodOptional<z.ZodString>;
1023
+ }, z.core.$strict>;
871
1024
  readonly offer_approval: z.ZodObject<{
872
1025
  task: z.ZodOptional<z.ZodString>;
873
1026
  card: z.ZodOptional<z.ZodInt>;
@@ -932,7 +1085,9 @@ export declare const LEAD_TOOL_READ_INPUTS: {
932
1085
  add_card: "add_card";
933
1086
  add_trigger: "add_trigger";
934
1087
  allow_browser: "allow_browser";
1088
+ allow_unattended: "allow_unattended";
935
1089
  approve: "approve";
1090
+ archive: "archive";
936
1091
  assign: "assign";
937
1092
  cancel_card: "cancel_card";
938
1093
  choose: "choose";
@@ -949,11 +1104,16 @@ export declare const LEAD_TOOL_READ_INPUTS: {
949
1104
  send_back: "send_back";
950
1105
  starter: "starter";
951
1106
  turn_off_browser: "turn_off_browser";
1107
+ turn_off_unattended: "turn_off_unattended";
952
1108
  unfollow: "unfollow";
953
1109
  }>;
954
1110
  repo: z.ZodOptional<z.ZodString>;
955
1111
  flow: z.ZodOptional<z.ZodInt>;
956
1112
  card: z.ZodOptional<z.ZodInt>;
1113
+ days: z.ZodOptional<z.ZodInt>;
1114
+ budget: z.ZodOptional<z.ZodString>;
1115
+ runs: z.ZodOptional<z.ZodString>;
1116
+ include: z.ZodOptional<z.ZodArray<z.ZodInt>>;
957
1117
  kit: z.ZodOptional<z.ZodEnum<{
958
1118
  [x: string]: string;
959
1119
  }>>;
@@ -1709,6 +1869,19 @@ export declare const LEAD_TOOL_OUTPUTS: {
1709
1869
  label: z.ZodString;
1710
1870
  action: z.ZodString;
1711
1871
  }, z.core.$loose>;
1872
+ readonly show_card: z.ZodObject<{
1873
+ card: z.ZodNumber;
1874
+ title: z.ZodString;
1875
+ shows: z.ZodArray<z.ZodString>;
1876
+ }, z.core.$loose>;
1877
+ readonly get_findings: z.ZodObject<{
1878
+ findings: z.ZodArray<z.ZodObject<{
1879
+ finding: z.ZodNumber;
1880
+ task: z.ZodString;
1881
+ state: z.ZodString;
1882
+ }, z.core.$loose>>;
1883
+ rule: z.ZodString;
1884
+ }, z.core.$loose>;
1712
1885
  readonly offer_approval: z.ZodObject<{
1713
1886
  offered: z.ZodBoolean;
1714
1887
  }, z.core.$loose>;
@@ -11,7 +11,9 @@
11
11
  import { z } from "zod";
12
12
  import { CHAT_ACTIONS } from "../chat-actions.js";
13
13
  import { RESULT_IMAGES_PER_TURN_CAP } from "../chat-evidence.js";
14
+ import { CARD_CATALOG, CARD_SOURCES, CARD_TASK_STATES } from "../card-catalog.js";
14
15
  import { CHAT_CONTROLS } from "../chat-controls.js";
16
+ import { CARD_CHART_TYPES, CARD_LIMITS } from "./chat-card.js";
15
17
  import { CHAT_TASK_ACTIONS } from "../chat-task-actions.js";
16
18
  import { LIMITS } from "../decision.js";
17
19
  import { CHECK_RESULTS, RUN_OUTCOMES, TASK_STATES } from "../lead-commitments.js";
@@ -51,6 +53,13 @@ export const CODE_MAP_VIEWS = ["overview", "area", "file", "search"];
51
53
  export const LISTED_TASK_STATES = ["queued", "running", "done", "failed", "cancelled"];
52
54
  /** The shared actions the lead proposes with propose_action; flows and subagents have their own tools. */
53
55
  const ACTION_OPERATIONS = Object.keys(CHAT_ACTIONS).filter(one => !one.startsWith("flow_") && !one.startsWith("subagent_"));
56
+ /** A card's spec as the lead sees it: one flat props object. Each element's own props are read strictly by card-catalog.ts. */
57
+ const cardProps = z.strictObject({
58
+ source: z.enum(CARD_SOURCES).optional(), chart: z.enum(CARD_CHART_TYPES).optional(), columns: z.array(z.string().max(40)).max(CARD_LIMITS.columns).optional(), facts: z.array(z.string().max(40)).max(CARD_LIMITS.facts).optional(),
59
+ repo: repoId.optional(), state: z.enum(CARD_TASK_STATES).optional(), limit: z.int().min(1).max(CARD_LIMITS.rows).optional(), flow: id.optional(), days: z.int().min(1).max(90).optional(),
60
+ text: z.string().max(CARD_LIMITS.text).optional(), control: enumOf(Object.keys(CHAT_CONTROLS)).optional(), task: taskRef.optional(), run: id.optional(), proposal: id.optional(),
61
+ });
62
+ const cardSpec = z.strictObject({ root: z.string().max(40), elements: z.object({}).catchall(z.strictObject({ type: enumOf(Object.keys(CARD_CATALOG.data.components)), props: cardProps, children: z.array(z.string().max(40)).max(16) })) });
54
63
  export const LEAD_TOOL_INPUTS = {
55
64
  get_brief: z.strictObject({ repo: repoId.optional() }),
56
65
  get_project_activity: z.strictObject({ repo: repoId, offset: offset.optional() }),
@@ -65,10 +74,14 @@ export const LEAD_TOOL_INPUTS = {
65
74
  title: text("actionTitle").optional(), id: z.string().optional(), run: id.optional(), note: note.optional(), catalog: text("actionName").optional(),
66
75
  name: text("actionName").optional(), command: text("actionCommand").optional(), args: z.array(text("actionCommand")).max(40).optional(),
67
76
  url: text("actionUrl").optional(), secrets: z.array(text("actionSecret")).max(12).optional(), about: text("actionAbout").optional(), on: z.boolean().optional(),
77
+ finding: id.optional(), summary: text("actionAbout").optional(), file: text("actionName").optional(), line: id.optional(), pointer: z.string().max(40).optional(),
78
+ revision: id.optional(), incident: id.optional(), provider: z.string().max(40).optional(), runner: text("actionName").optional(), persona: note.optional(), proposal: id.optional(), decision: z.enum(["accept", "reject"]).optional(), reason: text("actionAbout").optional(),
68
79
  }),
69
80
  propose_task_action: z.strictObject({ task: taskRef, operation: enumOf(Object.keys(CHAT_TASK_ACTIONS)), dependency: taskRef.optional(), run: id.optional() }),
70
81
  get_controls: z.strictObject({}),
71
82
  show_control: z.strictObject({ control: enumOf(Object.keys(CHAT_CONTROLS)), task: taskRef.optional(), repo: repoId.optional(), run: id.optional() }),
83
+ show_card: z.strictObject({ spec: cardSpec }),
84
+ get_findings: z.strictObject({ task: taskRef.optional(), repo: repoId.optional() }),
72
85
  offer_approval: z.strictObject({ task: taskRef.optional(), card: id.optional() }),
73
86
  get_result: z.strictObject({ task: taskRef, run: id.optional(), feedback_offset: offset.optional() }),
74
87
  get_diff: z.strictObject({ task: taskRef, run: id.optional(), file: text("diffPath").min(1).optional(), offset: offset.optional() }),
@@ -215,6 +228,8 @@ export const LEAD_TOOL_OUTPUTS = {
215
228
  propose_task_action: z.looseObject({ proposal: int, action: str, awaiting: str }),
216
229
  get_controls: z.looseObject({ confirmedInChat: z.array(str), existingControls: rows({ id: str, label: str, needsTask: z.boolean(), needsProject: z.boolean() }), rule: str }),
217
230
  show_control: z.looseObject({ card: int, label: str, action: str }),
231
+ show_card: z.looseObject({ card: int, title: str, shows: z.array(str) }),
232
+ get_findings: z.looseObject({ findings: z.array(z.looseObject({ finding: int, task: str, state: str })), rule: str }),
218
233
  offer_approval: z.looseObject({ offered: z.boolean() }),
219
234
  get_result: z.looseObject({
220
235
  task: str, root: str, currentExecution: str, run: int, title: str, feedback: list, feedbackTotal: int, nextFeedbackOffset: nullable(int),
@@ -170,6 +170,9 @@ declare const STORE_COLUMN_VALUES: {
170
170
  code_map_switch: "code_map_switch";
171
171
  decision_record: "decision_record";
172
172
  decision_retire: "decision_retire";
173
+ finding_resend: "finding_resend";
174
+ finding_send: "finding_send";
175
+ flow_archive: "flow_archive";
173
176
  flow_browser: "flow_browser";
174
177
  flow_browser_off: "flow_browser_off";
175
178
  flow_card_add: "flow_card_add";
@@ -189,13 +192,22 @@ declare const STORE_COLUMN_VALUES: {
189
192
  flow_trigger_pause: "flow_trigger_pause";
190
193
  flow_trigger_remove: "flow_trigger_remove";
191
194
  flow_trigger_resume: "flow_trigger_resume";
195
+ flow_unattended: "flow_unattended";
196
+ flow_unattended_off: "flow_unattended_off";
197
+ incident_resolve: "incident_resolve";
192
198
  kit_setup: "kit_setup";
193
199
  knowledge_instructions: "knowledge_instructions";
194
200
  knowledge_remove: "knowledge_remove";
195
201
  knowledge_restore: "knowledge_restore";
196
202
  knowledge_save: "knowledge_save";
197
203
  lead_about_you: "lead_about_you";
204
+ lead_identity: "lead_identity";
205
+ memory_decide: "memory_decide";
206
+ mode_revoke: "mode_revoke";
207
+ provider_resume: "provider_resume";
198
208
  result_accept: "result_accept";
209
+ revision_accept: "revision_accept";
210
+ revision_reject: "revision_reject";
199
211
  scope_approve: "scope_approve";
200
212
  skill_disable: "skill_disable";
201
213
  skill_enable: "skill_enable";
@@ -215,11 +227,15 @@ declare const STORE_COLUMN_VALUES: {
215
227
  suggestion_not_now: "suggestion_not_now";
216
228
  sweep_cancel_rest: "sweep_cancel_rest";
217
229
  sweep_file: "sweep_file";
230
+ task_archive: "task_archive";
218
231
  task_cancel: "task_cancel";
232
+ task_reopen: "task_reopen";
233
+ task_restore: "task_restore";
219
234
  task_resume: "task_resume";
220
235
  tool_add: "tool_add";
221
236
  tool_remove: "tool_remove";
222
237
  trial_stop: "trial_stop";
238
+ worker_note: "worker_note";
223
239
  }>;
224
240
  request: z.ZodObject<{}, z.core.$catchall<z.ZodUnknown>>;
225
241
  repo: z.ZodString;
@@ -1,6 +1,9 @@
1
1
  /** Discord messages and buttons transport the shared assistant's saved actions. */
2
2
  import { chatQuestionButtons } from "./subagent-question.js";
3
3
  import { chatAskButtons } from "./chat-ask.js";
4
+ import { discordCardEmbed } from "./card-render.js";
5
+ import { withoutCard } from "./chat-card-part.js";
6
+ import { cardWithin } from "./chat-card.js";
4
7
  import { chatFlowButtons } from "./chat-flow.js";
5
8
  import { refuseResultShots, resultShotsPruned, shotWaitsUntil } from "./result-shots.js";
6
9
  import { admitChatEvent } from "./chat-admission.js";
@@ -197,6 +200,10 @@ export async function deliverDiscordPart(options) {
197
200
  if (content.task &&
198
201
  !repos.includes(store.lookupRef(content.task)?.repo ?? ""))
199
202
  throw new DiscordError("Connected projects changed");
203
+ if (content.rich && !cardWithin(content.rich, repos)) {
204
+ state.dropPart(row.id, "Connected projects changed");
205
+ return true;
206
+ }
200
207
  let text = content.text, buttons = [], file;
201
208
  if (content.image && content.shot && resultShotsPruned(options.evidenceRoot, content.image.run)) {
202
209
  state.dropPart(row.id, "Removed by retention");
@@ -354,6 +361,8 @@ export async function deliverDiscordPart(options) {
354
361
  file.bytes = fresh.bytes;
355
362
  }
356
363
  const args = discordCard(text, buttons, content.voice === true && !content.proposal && !content.image);
364
+ if (content.rich)
365
+ args.embeds = [discordCardEmbed(content.rich, discordPlain)];
357
366
  if (file)
358
367
  args.attachments = [
359
368
  { id: 0, filename: file.name, description: text.slice(0, 1024) },
@@ -392,6 +401,11 @@ export async function deliverDiscordPart(options) {
392
401
  }
393
402
  // No permission to attach files here: one plain line instead of the result's screenshots.
394
403
  const saved = readChatPart(row.payload), content = saved.ok ? saved.value : null;
404
+ // Discord refused the card's embed: the part's own plain text goes instead, so the message is never lost.
405
+ if (content?.rich && error instanceof DiscordError && error.code === "Discord could not complete the request" && error.retryMs === 60_000) {
406
+ state.repaint(row.id, withoutCard(content));
407
+ return true;
408
+ }
395
409
  if (content !== null && content.image && content.shot && error instanceof DiscordError && error.code === DISCORD_REFUSED) {
396
410
  refuseResultShots(state, store, row, content, "Discord", options.clock?.() ?? new Date());
397
411
  return true;
@@ -39,7 +39,7 @@ export type DispatchAction = "open-result" | "retry-task" | "place-task" | "writ
39
39
  | "resume-run";
40
40
  export type DispatchDiagnosisCode = "updating" | "complete" | "needs-verification" | "proof-refuted" | "review-pending" | "reviewing" | "review-failed"
41
41
  /** v50: every root review attempt ended without a review; nothing retries. */
42
- | "review-exhausted" | "cancelled" | "failed" | "running" | "vanished-run" | "retry-scheduled" | "waiting-decision" | "waiting-incident" | "held"
42
+ | "review-exhausted" | "cancelled" | "failed" | "running" | "vanished-run" | "retry-scheduled" | "waiting-decision" | "waiting-incident" | "held" | "storage-blocked"
43
43
  /** v52: an operator stopped one exact attempt; only resuming it lifts the pause. */
44
44
  | "stopped" | "waiting-dependency" | "terminal-dependency" | "needs-project" | "needs-scope" | "needs-agent-profile" | "needs-approval" | "missing-requirement" | "no-worker-registered" | "no-worker-online" | "worker-at-capacity" | "provider-quota"
45
45
  /** The provider's sign-in stopped working; its dispatch is paused. */
package/dist/dispatch.js CHANGED
@@ -6,6 +6,7 @@
6
6
  * fleet observations on top for people and agents reading the queue. A
7
7
  * diagnosis can go stale; it can never grant a lease.
8
8
  */
9
+ import { STORAGE_HOLD_PREFIX } from "./needs-you.js";
9
10
  import { authorityStandsSql } from "./approval-authority.js";
10
11
  import { isAlive } from "./runner.js";
11
12
  import { updateAdmissionPaused, UPDATE_PAUSED } from "./desktop-update-gate.js";
@@ -161,7 +162,7 @@ function reviewViewOf(sourceRun, retry) {
161
162
  };
162
163
  }
163
164
  function approvedProfile(scope) {
164
- if (scope === null || scope.approvalKind === "chain")
165
+ if (scope === null)
165
166
  return null;
166
167
  return scope.approvedProfile ?? scope.profile ?? null;
167
168
  }
@@ -261,6 +262,11 @@ export function diagnoseTaskDispatch(store, taskId, now, readableRepos = null) {
261
262
  return answer("updating", "waiting", "Waiting for app update", UPDATE_PAUSED);
262
263
  const local = taskReadinessBlocker(store, ref.id, now);
263
264
  if (local?.code === "hold") {
265
+ // The storage check couldn't finish (dispose.ts holdForStorage): its own words, and Try again.
266
+ if (local.ownerKind === "backoff" && local.message.startsWith(`held: ${STORAGE_HOLD_PREFIX}`)) {
267
+ const when = local.until === null ? "" : ` It tries again on its own ${retryWhenWords(local.until, now)}.`;
268
+ return answer("storage-blocked", "waiting", "Needs macOS permission", `${local.message.slice(`held: ${STORAGE_HOLD_PREFIX}`.length)}${when}`, { action: "unhold", nextAt: local.until });
269
+ }
264
270
  if (local.ownerKind === "backoff") {
265
271
  // Run 2085: an attempt that stopped before its handoff says so plainly.
266
272
  const last = store.runsFor(ref.id).find(one => one.role === "builder" && one.finishedAt !== null);
package/dist/dispose.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { STORAGE_HOLD_PREFIX } from "./needs-you.js";
1
2
  import { type FailureClass } from "./claim.js";
2
3
  import type { BuildResult } from "./builder.js";
3
4
  import type { Store } from "./store.js";
@@ -89,6 +90,20 @@ export type Disposition = {
89
90
  * results and failed checks go straight to the person, as before. */
90
91
  export declare function maybeRequestAutoReview(store: Store, repo: string, runId: number, committed: boolean, noChange: boolean, now: Date, root?: string): void;
91
92
  export declare function disposeBuildOutcome(context: DisposeContext, result: BuildResult): Disposition;
93
+ /** How long a task waits after the storage check couldn't finish before it tries again on its own. */
94
+ export declare const STORAGE_RETRY_MS: number;
95
+ export { STORAGE_HOLD_PREFIX };
96
+ /**
97
+ * The storage HOLD: the check couldn't finish, so no launch can be fenced
98
+ * now. The task waits STORAGE_RETRY_MS and tries again on its own (a person
99
+ * who allowed access in Privacy & Security needn't do more), and "Try again"
100
+ * (unhold) lifts it at once. One message per task an hour, with the words.
101
+ */
102
+ export declare function holdForStorage(store: Store, args: {
103
+ taskRef: number;
104
+ taskId: string;
105
+ message: string;
106
+ }, now: Date): void;
92
107
  /**
93
108
  * The stale-approval HOLD, shared by the post-build disposition above and
94
109
  * the tick's pre-admission refusal (v48 integrity): a pre-routing row
@@ -152,6 +167,10 @@ export type RepairTrigger = {
152
167
  } | {
153
168
  kind: "stopped";
154
169
  reason: RepairStopReason;
170
+ }
171
+ /** The check runs once more on the same commit, with no agent: the failure looked unrelated to the change. */
172
+ | {
173
+ kind: "rerun";
155
174
  } | {
156
175
  kind: "none";
157
176
  };
package/dist/dispose.js CHANGED
@@ -1,4 +1,6 @@
1
1
  // Assignment tracking grants no repair authority or attempts.
2
+ import { STORAGE_HOLD_PREFIX } from "./needs-you.js";
3
+ import { blockAfterSignedOut } from "./fallback.js";
2
4
  import { authorizeCheckOnly } from "./check-only.js";
3
5
  import { proposeNewRoute } from "./route-change.js";
4
6
  import { originalTaskBase, focusedTestCommandSupported } from "./observations.js";
@@ -20,6 +22,7 @@ import { bodyHashOf, publicationBody } from "./publish.js";
20
22
  import { modeTermsFromJson } from "./modes.js";
21
23
  import { evidenceRoot, readVerifiedArtifact, writeEvidenceFile } from "./evidence.js";
22
24
  import { queueBuildReview } from "./build-review.js";
25
+ import { followBuiltRun } from "./check-repair.js";
23
26
  import { homedir } from "node:os";
24
27
  import { nightlyTaskRef, releaseTaskRef } from "./store.js";
25
28
  import {} from "./proof.js";
@@ -202,6 +205,7 @@ function disposeBuildOutcomeLocked(context, result) {
202
205
  store.resetStrikes(taskRef);
203
206
  store.clearQuota(runner, provider, model ?? "");
204
207
  maybeRequestAutoReview(store, repo, runId, result.committed, result.noChange === true, clock(), context.evidenceRoot);
208
+ followBuiltRun(store, repo, context.evidenceRoot ?? evidenceRoot(homedir()), runId, worktreePath, clock());
205
209
  return { kind: "built", committed: result.committed, noChange: result.noChange === true };
206
210
  }
207
211
  store.transact(() => {
@@ -331,11 +335,26 @@ function disposeBuildOutcomeLocked(context, result) {
331
335
  sealed: sealed.ok,
332
336
  };
333
337
  }
338
+ if (result.reason === "protected-storage") {
339
+ // The storage check couldn't finish (protected-storage.ts): nothing spent,
340
+ // no strike. The words are kept on the run, the task holds and tries
341
+ // again on its own, and a person reads why and which program to allow.
342
+ if (leaseId !== undefined)
343
+ release(store, leaseId, clock());
344
+ if (result.message.trim() !== "")
345
+ store.recordOutcomeFacts(runId, { handoff: result.message });
346
+ store.finishRun(runId, { outcome: "refused", reason: "protected-storage", now: clock() });
347
+ holdForStorage(store, { taskRef, taskId, message: result.message }, clock());
348
+ return { kind: "skipped", reason: "protected-storage" };
349
+ }
334
350
  // no-claim, not-yours, not-leased, protected-branch, wrong-branch:
335
351
  // invariants the dispatcher was supposed to uphold.
336
352
  if (leaseId !== undefined)
337
353
  release(store, leaseId, clock());
338
354
  store.finishRun(runId, { outcome: "refused", reason: result.reason, now: clock() });
355
+ // A fallback agent not signed in to its plan: the task waits for a person's retry, never loops (fallback.ts).
356
+ if (result.reason === "auth-mode" && blockAfterSignedOut(store, runId, clock()))
357
+ return { kind: "skipped", reason: "fallback-signed-out" };
339
358
  if (result.reason === "stale-approval") {
340
359
  // A deterministic refusal (setup review): the approval no longer
341
360
  // matches the routing, and nothing about the next pass changes that.
@@ -351,6 +370,27 @@ function disposeBuildOutcomeLocked(context, result) {
351
370
  }
352
371
  return { kind: "invariant", reason: result.reason };
353
372
  }
373
+ /** How long a task waits after the storage check couldn't finish before it tries again on its own. */
374
+ export const STORAGE_RETRY_MS = 10 * 60_000;
375
+ export { STORAGE_HOLD_PREFIX };
376
+ /**
377
+ * The storage HOLD: the check couldn't finish, so no launch can be fenced
378
+ * now. The task waits STORAGE_RETRY_MS and tries again on its own (a person
379
+ * who allowed access in Privacy & Security needn't do more), and "Try again"
380
+ * (unhold) lifts it at once. One message per task an hour, with the words.
381
+ */
382
+ export function holdForStorage(store, args, now) {
383
+ store.holdOwned({ taskRef: args.taskRef, ownerKind: "backoff", ownerId: `storage:${args.taskRef}`, reason: `${STORAGE_HOLD_PREFIX}${args.message}`, until: new Date(now.getTime() + STORAGE_RETRY_MS) }, now);
384
+ store.enqueueNotification({
385
+ source: { taskRef: args.taskRef },
386
+ dedupeKey: `protected-storage:${args.taskRef}:${now.toISOString().slice(0, 13)}`,
387
+ kind: "protected-storage",
388
+ pushClass: "attention",
389
+ link: `/t/${encodeURIComponent(args.taskId)}`,
390
+ subject: `${args.taskId}: waiting for macOS permission`,
391
+ body: `${args.message}\nIt tries again on its own in ${STORAGE_RETRY_MS / 60_000} minutes.`,
392
+ }, now);
393
+ }
354
394
  /**
355
395
  * The stale-approval HOLD, shared by the post-build disposition above and
356
396
  * the tick's pre-admission refusal (v48 integrity): a pre-routing row
@@ -544,9 +584,9 @@ export function regateTask(store, taskId, now, approval) {
544
584
  export function maybeTriggerRepair(store, repo, evidenceRoot, sourceRunId, verdict, now, cause = "review", automatic = false) {
545
585
  if (verdict !== "short" && verdict !== "refuted")
546
586
  return { kind: "none" };
547
- // Findings go to the lead. Explicit repair/revision actions remain available.
548
- // Historical mode signatures remain intact; no automatic follow-up spends.
549
- if (automatic)
587
+ // A review's findings go to the lead, and a person asks for their repair. A failed check is the one automatic road
588
+ // (check-repair.ts), and only under a mode a person signed for it, checked below.
589
+ if (automatic && cause !== "verification")
550
590
  return { kind: "none" };
551
591
  // One attempt per source run, ever (source_run UNIQUE) — checked first so
552
592
  // a re-fired trigger is a silent no-op, never a duplicate.
@@ -604,9 +644,9 @@ export function maybeTriggerRepair(store, repo, evidenceRoot, sourceRunId, verdi
604
644
  const terms = mode === null ? null : modeTermsFromJson(mode.termsJson);
605
645
  let verification;
606
646
  if (cause === "verification") {
607
- // A signed repair mode is required for this automatic producer. The
608
- // existing review/manual road continues to offer unapproved drafts.
609
- if (terms?.repairAuto !== true || run.finishedAt === null || !run.headRevision ||
647
+ // A signed repair mode is required for this automatic producer. A person
648
+ // asking (check-repair.ts startCheckRepair) gets an unapproved draft instead.
649
+ if ((automatic && terms?.repairAuto !== true) || run.finishedAt === null || !run.headRevision ||
610
650
  (run.outcome !== "built" && run.outcome !== "no-change"))
611
651
  return { kind: "none" };
612
652
  const owner = store.refForId(run.taskRef);
@@ -639,7 +679,7 @@ export function maybeTriggerRepair(store, repo, evidenceRoot, sourceRunId, verdi
639
679
  subject: unrelated.kind === "gate-timed-out" ? "The project check ran out of time; running it once more" : "A slow test outside this change failed the check; running it once more",
640
680
  body: `${gateFailureSummary(unrelated)} The check runs again once on the same commit ${run.headRevision.slice(0, 7)}, with no agent. If it fails again, a person decides.`,
641
681
  link: `/t/${encodeURIComponent(taskId)}` }, now);
642
- return { kind: "none" };
682
+ return { kind: "rerun" };
643
683
  }
644
684
  }
645
685
  store.enqueueNotification({ source: { run: sourceRunId }, dedupeKey: `repair-unrelated:${sourceRunId}`, kind: "repair-evidence", pushClass: "attention",
@@ -19,6 +19,7 @@
19
19
  * The build's code never runs in the oracle's own process, and nothing the build wrote (a result file, a verdict) is
20
20
  * ever read as one: the verdict travels from the oracle to the runner over the oracle's own pipe.
21
21
  */
22
+ import { prepareProtectedStorage } from "./protected-storage.js";
22
23
  import { spawn } from "node:child_process";
23
24
  import { createHash, randomBytes } from "node:crypto";
24
25
  import { closeSync, constants, lstatSync, mkdirSync, mkdtempSync, openSync, realpathSync, rmSync, writeSync } from "node:fs";
@@ -55,10 +56,12 @@ export function canonical(value) {
55
56
  export const fencedGradeRunner = async ({ cwd, stdin, timeoutMs, deny }) => {
56
57
  if (!macosFenceAvailable() && !linuxFenceAvailable())
57
58
  throw new Error("this machine can't fence the graded program");
59
+ // The storage check, awaited off this thread (protected-storage.ts); a refusal keeps its words.
60
+ const storage = await prepareProtectedStorage();
58
61
  const egress = await startEgressProxy({ hosts: [] });
59
62
  try {
60
63
  const network = { mode: "allowlist", proxyPort: egress.port, brokerPorts: [], loopback: "proxy-only", socketRoots: [cwd], token: egress.secret };
61
- const launch = fenceFor("check", { databaseFile: null, worktree: cwd, boundary: null, binding: null, runFolders: runFolders(null), poolRoots: [], extra: [...deny],
64
+ const launch = fenceFor("check", { databaseFile: null, worktree: cwd, boundary: null, binding: null, runFolders: runFolders(null), poolRoots: [], storage, extra: [...deny],
62
65
  writes: { writes: { subpaths: [cwd], prefixes: [] }, readOnly: [] }, network });
63
66
  const result = await run(process.execPath, ["probe.mjs"], { cwd, stdin, timeoutMs, maxBuffer: ORACLE_OUTPUT_BYTES, processGroup: true, envAllowlist: ["PATH"],
64
67
  env: { ...proxyEnv(egress, "proxy-only"), HOME: cwd, TMPDIR: cwd }, fence: launch.fence, fenceGrants: launch.fenceGrants, confinement: "required" });
@@ -0,0 +1,53 @@
1
+ import type { Store } from "./store.js";
2
+ export type EvidenceReading = "failing" | "fixed" | "still-failing" | "check-changed" | "out-of-date";
3
+ export type EvidenceSide = {
4
+ run: number;
5
+ head: string;
6
+ status: "passed" | "failed";
7
+ via: "build" | "follow-up";
8
+ screenshots: number[];
9
+ };
10
+ export type EvidencePair = {
11
+ id: number;
12
+ familyRoot: string;
13
+ before: EvidenceSide;
14
+ receiptDigest: string;
15
+ commandDigest: string | null;
16
+ savedBy: string;
17
+ savedAt: string;
18
+ after: EvidenceSide | null;
19
+ latestHead: string | null;
20
+ latestRun: number | null;
21
+ reading: EvidenceReading;
22
+ };
23
+ export declare function evidencePairsOf(store: Store, root: string, taskId: string, now: Date): EvidencePair[];
24
+ export type SavePair = {
25
+ ok: true;
26
+ id: number;
27
+ existing: boolean;
28
+ } | {
29
+ ok: false;
30
+ reason: "unknown" | "not-on-task" | "no-failed-check";
31
+ message: string;
32
+ };
33
+ /** Save a run's failed check as the before side. Only a sealed failure can be saved: there's nothing else to compare. */
34
+ export declare function saveEvidencePair(store: Store, root: string, input: {
35
+ taskId: string;
36
+ runId?: number;
37
+ by: string;
38
+ }, now: Date): SavePair;
39
+ export type RerunPair = {
40
+ ok: true;
41
+ run: number;
42
+ head: string;
43
+ request: number;
44
+ existing: boolean;
45
+ } | {
46
+ ok: false;
47
+ message: string;
48
+ };
49
+ /** Ask for the same check on the latest commit. Its level is the one the saved failure ran at. The caller proves the person. */
50
+ export declare function rerunEvidence(store: Store, taskId: string, actor: string, now: Date): RerunPair;
51
+ /** How a pair reads, in one sentence. */
52
+ export declare function evidenceWords(pair: EvidencePair): string;
53
+ export { askFailingFirst, dropFailingFirst, FAILING_FIRST_FILE, failingFirstOf, failingFirstReportSchema, failingFirstWords, saveFailingFirstReport, type FailingFirst } from "./failing-first.js";