toolroll 0.9.34 → 0.9.37

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 (195) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/README.md +7 -0
  3. package/dist/assignment-adapters.d.ts +12 -48
  4. package/dist/assignment-adapters.js +33 -43
  5. package/dist/assignment.d.ts +1 -0
  6. package/dist/assignment.js +2 -2
  7. package/dist/browser-workspace.d.ts +3 -3
  8. package/dist/build-review.d.ts +1 -1
  9. package/dist/build-review.js +2 -1
  10. package/dist/builder.js +4 -7
  11. package/dist/chat-acceptance.d.ts +2 -2
  12. package/dist/chat-actions.d.ts +18 -12
  13. package/dist/chat-actions.js +15 -65
  14. package/dist/chat-ask.js +5 -5
  15. package/dist/chat-decide.d.ts +14 -1
  16. package/dist/chat-decide.js +12 -6
  17. package/dist/chat-delivery-state.d.ts +12 -70
  18. package/dist/chat-delivery-state.js +17 -135
  19. package/dist/chat-delivery.js +58 -30
  20. package/dist/chat-flow.js +8 -6
  21. package/dist/chat-inbox.js +1 -1
  22. package/dist/coding-context.d.ts +2 -39
  23. package/dist/coding-context.js +2 -1
  24. package/dist/coding-handoff.d.ts +6 -25
  25. package/dist/coding-handoff.js +11 -5
  26. package/dist/coding-types.d.ts +2 -26
  27. package/dist/coding-workspace.d.ts +2 -0
  28. package/dist/coding-workspace.js +50 -14
  29. package/dist/contracts/acceptance-terms.d.ts +14 -0
  30. package/dist/contracts/acceptance-terms.js +16 -0
  31. package/dist/contracts/chat-actions.d.ts +328 -0
  32. package/dist/contracts/chat-actions.js +133 -0
  33. package/dist/contracts/chat-callback-rows.d.ts +59 -0
  34. package/dist/contracts/chat-callback-rows.js +46 -0
  35. package/dist/contracts/chat-content.d.ts +276 -0
  36. package/dist/contracts/chat-content.js +127 -0
  37. package/dist/contracts/chat-tables.d.ts +6 -0
  38. package/dist/contracts/chat-tables.js +136 -0
  39. package/dist/contracts/coding-context.d.ts +97 -0
  40. package/dist/contracts/coding-context.js +60 -0
  41. package/dist/contracts/coding-handoff.d.ts +44 -0
  42. package/dist/contracts/coding-handoff.js +47 -0
  43. package/dist/contracts/coding-workspace.d.ts +165 -0
  44. package/dist/contracts/coding-workspace.js +71 -0
  45. package/dist/contracts/contract.d.ts +40 -7
  46. package/dist/contracts/contract.js +155 -19
  47. package/dist/contracts/decision.d.ts +45 -0
  48. package/dist/contracts/decision.js +45 -0
  49. package/dist/contracts/discord-callback.d.ts +45 -0
  50. package/dist/contracts/discord-callback.js +34 -0
  51. package/dist/contracts/flow-card.d.ts +50 -0
  52. package/dist/contracts/flow-card.js +46 -0
  53. package/dist/contracts/flow-propose.d.ts +325 -0
  54. package/dist/contracts/flow-propose.js +41 -0
  55. package/dist/contracts/flow-send.d.ts +119 -0
  56. package/dist/contracts/flow-send.js +112 -0
  57. package/dist/contracts/flow.d.ts +19405 -0
  58. package/dist/contracts/flow.js +364 -0
  59. package/dist/contracts/gateway-tools.d.ts +374 -0
  60. package/dist/contracts/gateway-tools.js +94 -0
  61. package/dist/contracts/handoff.d.ts +91 -0
  62. package/dist/contracts/handoff.js +82 -0
  63. package/dist/contracts/lead-context.d.ts +165 -0
  64. package/dist/contracts/lead-context.js +73 -0
  65. package/dist/contracts/lead-tools.d.ts +1905 -0
  66. package/dist/contracts/lead-tools.js +265 -0
  67. package/dist/contracts/memory-pass.d.ts +158 -0
  68. package/dist/contracts/memory-pass.js +102 -0
  69. package/dist/contracts/project-knowledge.d.ts +126 -0
  70. package/dist/contracts/project-knowledge.js +93 -0
  71. package/dist/contracts/project-memory.d.ts +117 -0
  72. package/dist/contracts/project-memory.js +51 -0
  73. package/dist/contracts/project-skills.d.ts +121 -0
  74. package/dist/contracts/project-skills.js +76 -0
  75. package/dist/contracts/proof.d.ts +136 -0
  76. package/dist/contracts/proof.js +110 -0
  77. package/dist/contracts/review-findings.d.ts +46 -0
  78. package/dist/contracts/review-findings.js +48 -0
  79. package/dist/contracts/route.d.ts +484 -0
  80. package/dist/contracts/route.js +128 -0
  81. package/dist/contracts/scope.d.ts +105 -0
  82. package/dist/contracts/scope.js +114 -0
  83. package/dist/contracts/scout-report.d.ts +234 -0
  84. package/dist/contracts/scout-report.js +141 -0
  85. package/dist/contracts/slack-callback.d.ts +43 -0
  86. package/dist/contracts/slack-callback.js +35 -0
  87. package/dist/contracts/sort-answer.d.ts +83 -0
  88. package/dist/contracts/sort-answer.js +77 -0
  89. package/dist/contracts/stage-output.d.ts +127 -0
  90. package/dist/contracts/stage-output.js +166 -0
  91. package/dist/contracts/task-sizing.d.ts +24 -0
  92. package/dist/contracts/task-sizing.js +28 -0
  93. package/dist/contracts/teammate-turn.d.ts +43 -0
  94. package/dist/contracts/teammate-turn.js +51 -0
  95. package/dist/contracts/teams-callback.d.ts +14 -0
  96. package/dist/contracts/teams-callback.js +14 -0
  97. package/dist/contracts/telegram-callback.d.ts +120 -0
  98. package/dist/contracts/telegram-callback.js +97 -0
  99. package/dist/contracts/verification-receipt.d.ts +148 -0
  100. package/dist/contracts/verification-receipt.js +87 -0
  101. package/dist/decision.d.ts +11 -20
  102. package/dist/decision.js +86 -167
  103. package/dist/demo.js +28 -11
  104. package/dist/discord-chat.js +31 -20
  105. package/dist/evidence.d.ts +3 -40
  106. package/dist/evidence.js +5 -1
  107. package/dist/flow-engine.js +14 -17
  108. package/dist/flow-gallery.d.ts +3 -2
  109. package/dist/flow-items.d.ts +4 -13
  110. package/dist/flow-items.js +1 -15
  111. package/dist/flow-send.d.ts +6 -37
  112. package/dist/flow-send.js +18 -14
  113. package/dist/flow-share.d.ts +10 -32
  114. package/dist/flow-share.js +72 -84
  115. package/dist/flow-sort.d.ts +7 -26
  116. package/dist/flow-sort.js +25 -20
  117. package/dist/flow-starters.d.ts +2 -1
  118. package/dist/flow-steps.js +3 -2
  119. package/dist/flow-triggers.d.ts +15 -72
  120. package/dist/flow-triggers.js +94 -83
  121. package/dist/flows.d.ts +37 -220
  122. package/dist/flows.js +356 -387
  123. package/dist/invoke.js +4 -4
  124. package/dist/lead-context.d.ts +4 -2
  125. package/dist/lead-context.js +24 -17
  126. package/dist/mate-tools.d.ts +4 -0
  127. package/dist/mate-tools.js +111 -308
  128. package/dist/mcp-connect.d.ts +27 -4
  129. package/dist/mcp-connect.js +92 -17
  130. package/dist/mcp.js +84 -216
  131. package/dist/memory-pass.d.ts +17 -42
  132. package/dist/memory-pass.js +54 -26
  133. package/dist/operate.js +31 -2
  134. package/dist/phase-routing.d.ts +19 -72
  135. package/dist/phase-routing.js +34 -164
  136. package/dist/plan.d.ts +5 -5
  137. package/dist/plan.js +5 -25
  138. package/dist/process-recovery-eligibility.d.ts +2 -2
  139. package/dist/process-recovery-eligibility.js +13 -8
  140. package/dist/project-knowledge.d.ts +16 -35
  141. package/dist/project-knowledge.js +42 -21
  142. package/dist/project-memory.d.ts +2 -28
  143. package/dist/project-memory.js +28 -13
  144. package/dist/project-skills.d.ts +16 -38
  145. package/dist/project-skills.js +41 -32
  146. package/dist/project-tools.d.ts +1 -0
  147. package/dist/project-tools.js +4 -4
  148. package/dist/proof.d.ts +15 -91
  149. package/dist/proof.js +90 -249
  150. package/dist/provider.d.ts +2 -0
  151. package/dist/provider.js +4 -16
  152. package/dist/result-evidence-readers.d.ts +42 -0
  153. package/dist/result-evidence-readers.js +91 -0
  154. package/dist/result-shots.d.ts +1 -1
  155. package/dist/result-shots.js +2 -1
  156. package/dist/review-switch.d.ts +3 -8
  157. package/dist/reviewer.d.ts +8 -12
  158. package/dist/reviewer.js +11 -35
  159. package/dist/scope.d.ts +21 -43
  160. package/dist/scope.js +42 -128
  161. package/dist/scout-report.d.ts +9 -194
  162. package/dist/scout-report.js +113 -264
  163. package/dist/scout.js +15 -11
  164. package/dist/serve.js +17 -101
  165. package/dist/slack-chat.js +26 -20
  166. package/dist/slack-state.js +2 -1
  167. package/dist/store.d.ts +9 -32
  168. package/dist/store.js +18 -15
  169. package/dist/structured-output.js +2 -1
  170. package/dist/subscription-chat.d.ts +4 -0
  171. package/dist/subscription-chat.js +4 -1
  172. package/dist/surface.js +8 -1
  173. package/dist/task-review-brief.d.ts +69 -0
  174. package/dist/task-review-brief.js +179 -0
  175. package/dist/task-sizing.d.ts +3 -27
  176. package/dist/task-sizing.js +11 -26
  177. package/dist/task-text.d.ts +0 -5
  178. package/dist/task-text.js +1 -3
  179. package/dist/teammate-question.js +7 -5
  180. package/dist/teammate-work.js +9 -5
  181. package/dist/teammates.d.ts +13 -64
  182. package/dist/teammates.js +32 -33
  183. package/dist/teams-chat.js +20 -11
  184. package/dist/telegram-flow.js +4 -3
  185. package/dist/telegram-mate.d.ts +4 -14
  186. package/dist/telegram-mate.js +8 -7
  187. package/dist/telegram-team.d.ts +2 -19
  188. package/dist/telegram.d.ts +1 -1
  189. package/dist/telegram.js +52 -42
  190. package/dist/text-limits.d.ts +212 -1
  191. package/dist/text-limits.js +214 -1
  192. package/dist/tools-ui.d.ts +2 -1
  193. package/dist/tools-ui.js +7 -4
  194. package/dist/verification-evidence.js +24 -12
  195. package/package.json +1 -1
@@ -16,8 +16,81 @@ export declare const TEXT_LIMITS: {
16
16
  readonly noteBytes: 16000;
17
17
  /** A flow work zone's instructions; nothing is held back for the card's details (they are attached when long). */
18
18
  readonly flowInstructions: 8000;
19
+ /** A flow's zones and steps (src/contracts/flow.ts): a zone's name, a step's id as written, and a reference to another step. */
20
+ readonly flowTitle: 60;
21
+ readonly flowStepId: 32;
22
+ readonly flowRef: 60;
23
+ /** A Message zone's message, or the comment an Update zone leaves. */
24
+ readonly flowMessage: 1000;
25
+ /** Who decides a "Person decides" zone (a sign-in name), and the AI teammate who works a zone, as written. */
26
+ readonly flowDecider: 64;
27
+ readonly flowTeammate: 40;
28
+ /** A Build zone's other project: its path. */
29
+ readonly flowRepo: 1000;
30
+ /** A script's or teammate's answer, a choice's button, a Sort zone's answer and what it means, its question, and a score's level. */
31
+ readonly flowAnswer: 40;
32
+ readonly flowChoice: 40;
33
+ readonly flowSortMeans: 200;
34
+ readonly flowSortQuestion: 300;
35
+ readonly flowSortLevel: 120;
36
+ /** A Web request zone's address, one header's value and the body it sends (an email's body too). */
37
+ readonly flowUrl: 2000;
38
+ readonly flowHeader: 500;
39
+ readonly flowBody: 8000;
40
+ /** An email's recipients and subject. */
41
+ readonly flowEmailTo: 500;
42
+ readonly flowEmailSubject: 200;
43
+ /** A tool zone's tool (MCP server), its function, and the arguments as JSON. */
44
+ readonly flowToolServer: 64;
45
+ readonly flowToolName: 100;
46
+ readonly flowToolArgs: 4000;
47
+ /** A wait or reminder said in words ("3 days"), and a time zone's name. */
48
+ readonly flowDuration: 40;
49
+ readonly flowTimeZone: 60;
50
+ /** A flow's name, and a flow file's about line, needs, parameter questions and answers, and scripts. */
51
+ readonly flowName: 80;
52
+ readonly flowFileAbout: 600;
53
+ readonly flowFileNeed: 80;
54
+ readonly flowParameter: 200;
55
+ readonly flowScriptAbout: 160;
56
+ readonly flowScriptBody: 20000;
57
+ readonly flowScriptFile: 200;
58
+ /** A trigger's settings: a button's label and questions, a schedule and the card it makes, GitHub, Linear, webhook and email terms. */
59
+ readonly triggerButton: 40;
60
+ readonly triggerQuestion: 80;
61
+ readonly triggerSchedule: 80;
62
+ readonly triggerTitle: 200;
63
+ readonly triggerDescription: 2000;
64
+ readonly triggerGithubRepo: 140;
65
+ readonly triggerLabel: 50;
66
+ readonly triggerBranch: 100;
67
+ readonly triggerTeam: 12;
68
+ readonly triggerState: 40;
69
+ readonly triggerWebhookTitle: 120;
70
+ readonly triggerWebhookField: 80;
71
+ readonly triggerFolder: 100;
72
+ readonly triggerSender: 300;
73
+ readonly triggerSubject: 100;
74
+ /** A schedule's secrets written as one line ("API_KEY, CRM_TOKEN"). */
75
+ readonly triggerSecrets: 500;
76
+ /** A card the lead adds to a flow: its title and description; a script it saves inline. */
77
+ readonly flowCardTitle: 200;
78
+ readonly flowCardDescription: 4000;
79
+ readonly flowScriptInline: 1600;
19
80
  /** A research report's summary, in UTF-8 bytes. */
20
81
  readonly reportSummary: 2500;
82
+ /** A scout's whole report file (src/contracts/scout-report.ts), in UTF-8 bytes. */
83
+ readonly reportPayloadBytes: number;
84
+ /** A report's document (markdown), in UTF-8 bytes. */
85
+ readonly reportDocumentBytes: number;
86
+ /** A report's title, and a follow-up's or an item's title, in UTF-8 bytes. */
87
+ readonly reportTitleBytes: 200;
88
+ /** Why an item fits, in UTF-8 bytes. */
89
+ readonly reportWhyBytes: 1000;
90
+ /** A web address a report cites, in UTF-8 bytes. */
91
+ readonly reportUrlBytes: 2000;
92
+ /** A screenshot's caption and its file name, in UTF-8 bytes. */
93
+ readonly reportCaptionBytes: 300;
21
94
  /** What one flow step passes on to the steps after it. */
22
95
  readonly stageOutput: 12000;
23
96
  /** The planner's whole handoff file, in UTF-8 bytes. */
@@ -30,12 +103,150 @@ export declare const TEXT_LIMITS: {
30
103
  readonly planItem: 600;
31
104
  /** One path a plan expects to touch. */
32
105
  readonly planTouch: 200;
106
+ /** How many paths a task's scope may say it touches (a plan says at most 32 of them). A count, not a length. */
107
+ readonly scopeTouches: 50;
108
+ /** Why a task was sized as it was, as its sealed route keeps it. */
109
+ readonly sizeReason: 300;
33
110
  /** A planner's amendment note: why the filed contract must change. */
34
111
  readonly planAmendment: 1000;
35
112
  /** An acceptance criterion's id, statement and advisory how, in UTF-8 bytes. */
36
113
  readonly acceptanceIdBytes: 40;
37
114
  readonly acceptanceStatementBytes: 1000;
38
115
  readonly acceptanceHowBytes: 500;
116
+ /** A builder's whole proof file, in UTF-8 bytes. */
117
+ readonly proofPayloadBytes: number;
118
+ /** A proof criterion's id, in UTF-8 bytes. */
119
+ readonly proofCriterionIdBytes: 40;
120
+ /** How a proof criterion was met, in UTF-8 bytes. */
121
+ readonly proofHowBytes: 500;
122
+ /** One proof line — a criterion statement, evidence ref, check command or summary, changed path, caveat, or
123
+ * screenshot path or caption — in UTF-8 bytes. */
124
+ readonly proofLineBytes: 300;
125
+ /** The task sizing classifier's reason (src/contracts/task-sizing.ts). */
126
+ readonly sizingReason: 160;
127
+ /** A review comment's or build-review finding's path, and its note or failure scenario (src/contracts/review-findings.ts). */
128
+ readonly reviewPath: 300;
129
+ readonly reviewNote: 500;
130
+ /** A parked decision (src/contracts/decision.ts): its recap and question, an option's id, label and consequence, and who it is for. */
131
+ readonly decisionRecap: 2000;
132
+ readonly decisionQuestion: 2000;
133
+ readonly decisionOptionId: 40;
134
+ /** A decision's recommendation: an option's id, optionally followed by ": " and why. */
135
+ readonly decisionRecommendation: 500;
136
+ readonly decisionLabel: 120;
137
+ readonly decisionConsequence: 500;
138
+ readonly decisionAssignee: 120;
139
+ /** A teammate's turn (src/contracts/teammate-turn.ts): the answer it picks (and each option it offers), its question, its reason and what it remembers. */
140
+ readonly teammateAnswer: 60;
141
+ readonly teammateQuestion: 600;
142
+ readonly teammateReason: 400;
143
+ readonly teammateRemember: 300;
144
+ /** The lead's tools and the MCP gateway (src/contracts/lead-tools.ts, gateway-tools.ts): a task's id as a tool names
145
+ * it, a task's title and one path it touches, and a project's path as the gateway names it (an assignment brief or
146
+ * project context, and the rest of the gateway). */
147
+ readonly taskRef: 64;
148
+ readonly taskTitle: 200;
149
+ readonly taskTouch: 200;
150
+ readonly projectPath: 4096;
151
+ readonly gatewayRepo: 800;
152
+ /** A project-context query, a check-log search, a memory search, a task search and a changed file's path. */
153
+ readonly contextQuery: 1000;
154
+ readonly logSearch: 120;
155
+ readonly memorySearch: 300;
156
+ readonly taskSearch: 200;
157
+ readonly diffPath: 300;
158
+ /** A hold's or cancel's reason, an answer's rationale and option, a worker's name, an agent change's reason, a
159
+ * provider and model name, how a task's checks were asked for, and an ISO time a recap counts from. */
160
+ readonly proposalReason: 200;
161
+ readonly answerRationale: 400;
162
+ readonly answerOption: 64;
163
+ readonly workerName: 60;
164
+ readonly agentWhy: 400;
165
+ readonly agentProvider: 20;
166
+ readonly agentModel: 120;
167
+ readonly checksWords: 80;
168
+ readonly recapSince: 30;
169
+ /** A shared action's fields as the lead proposes them (propose_action). */
170
+ readonly actionSample: 800;
171
+ readonly actionContent: 12000;
172
+ readonly actionTitle: 120;
173
+ readonly actionName: 40;
174
+ readonly actionCommand: 400;
175
+ readonly actionUrl: 500;
176
+ readonly actionSecret: 64;
177
+ readonly actionAbout: 240;
178
+ /** A teammate change (propose_teammate): a soul section's title, a routine's schedule, a tool, an action, a limit's
179
+ * field, a name, a soul file, a note, a choice and text. */
180
+ readonly teammateSection: 60;
181
+ readonly teammateSchedule: 80;
182
+ readonly teammateTool: 40;
183
+ readonly teammateAction: 64;
184
+ readonly teammateName: 40;
185
+ readonly teammateSoul: 12000;
186
+ readonly teammateNote: 300;
187
+ readonly teammateChoice: 60;
188
+ readonly teammateText: 2000;
189
+ /** A project knowledge reference id, a promise and why it is released, what the lead remembers (its text, a
190
+ * decision's reason and source), a person's name, and a question to the owner with its options. */
191
+ readonly knowledgeReference: 20;
192
+ readonly promise: 240;
193
+ readonly rememberText: 240;
194
+ readonly rememberWhy: 2000;
195
+ readonly rememberSource: 200;
196
+ readonly personName: 80;
197
+ readonly ownerQuestion: 200;
198
+ readonly ownerOption: 40;
199
+ /** A gateway filing's idempotency key. */
200
+ readonly idempotencyKey: 64;
201
+ /** The lead's catch-up bundle (src/contracts/lead-context.ts), in UTF-8 bytes, and the lines it clips: an open promise,
202
+ * a confirmed correction, the end of changed instructions a correction shows, and what an open proposal is about. */
203
+ readonly leadContextBytes: 8000;
204
+ readonly leadPromise: 160;
205
+ readonly leadCorrection: 300;
206
+ readonly leadCorrectionInstructions: 240;
207
+ readonly leadProposalAbout: 120;
208
+ /** Project knowledge (src/contracts/project-knowledge.ts), in UTF-8 bytes: the instructions (also the memory pass's
209
+ * budget for them), a reference's title, path and text, the context a run gets, the decision lines in it, and the
210
+ * repository excerpts added when at least the minimum is left. */
211
+ readonly knowledgeInstructionsBytes: 4000;
212
+ readonly knowledgeTitleBytes: 120;
213
+ readonly knowledgePathBytes: 300;
214
+ readonly knowledgeReferenceBytes: 12000;
215
+ readonly knowledgeContextBytes: 24000;
216
+ readonly knowledgeDecisionsBytes: 1500;
217
+ readonly knowledgeRepositoryBytes: 6000;
218
+ readonly knowledgeRepositoryMinBytes: 3000;
219
+ /** A project decision (src/contracts/project-memory.ts), in UTF-8 bytes: the choice, why, who decided, where it came
220
+ * from, and why it was retired. */
221
+ readonly decisionClaimBytes: 240;
222
+ readonly decisionWhyBytes: 2000;
223
+ readonly decisionByBytes: 80;
224
+ readonly decisionSourceBytes: 200;
225
+ readonly decisionRetireBytes: 500;
226
+ /** A skill package (src/contracts/project-skills.ts): its name, description, requirements and source in UTF-8 bytes,
227
+ * a file's path, SKILL.md, one file, the package and the enabled selection in bytes; a test's sample request and
228
+ * feedback in characters and bytes. */
229
+ readonly skillNameBytes: 64;
230
+ readonly skillDescriptionBytes: 1024;
231
+ readonly skillRequirementsBytes: 500;
232
+ readonly skillSourceBytes: 500;
233
+ readonly skillPath: 240;
234
+ readonly skillBodyBytes: number;
235
+ readonly skillFileBytes: number;
236
+ readonly skillPackageBytes: number;
237
+ readonly skillSelectionBytes: number;
238
+ readonly skillSample: 800;
239
+ readonly skillSampleBytes: 4000;
240
+ readonly skillFeedback: 500;
241
+ readonly skillFeedbackBytes: 2000;
242
+ /** The memory pass (src/contracts/memory-pass.ts): one session's trace, and in its verdict an effect, a mistake and
243
+ * the instruction it proposes. */
244
+ readonly memoryTrace: 40000;
245
+ readonly memoryEffect: 300;
246
+ readonly memoryMistake: 300;
247
+ readonly memoryInstruction: 240;
248
+ /** A Telegram button's callback_data, in UTF-8 bytes: Telegram refuses a button with more. */
249
+ readonly telegramCallbackDataBytes: 64;
39
250
  };
40
251
  export type TextLimitKey = keyof typeof TEXT_LIMITS;
41
252
  /** One chat message on each platform. Longer text is split across messages, never cut. Teams: the text one Adaptive Card
@@ -48,7 +259,7 @@ export declare const PLATFORM_LIMITS: {
48
259
  };
49
260
  export type Platform = keyof typeof PLATFORM_LIMITS;
50
261
  /** The line a model reads before it writes a field with a limit. */
51
- export declare function limitRule(what: string, limit: number, unit?: "characters" | "bytes"): string;
262
+ export declare function limitRule(what: string, limit: number, unit?: "characters" | "bytes", shorten?: boolean): string;
52
263
  /** One field over its limit: what it is, its limit, and how long it came back. */
53
264
  export type Overrun = {
54
265
  field: string;
@@ -16,8 +16,81 @@ export const TEXT_LIMITS = {
16
16
  noteBytes: 16_000,
17
17
  /** A flow work zone's instructions; nothing is held back for the card's details (they are attached when long). */
18
18
  flowInstructions: 8_000,
19
+ /** A flow's zones and steps (src/contracts/flow.ts): a zone's name, a step's id as written, and a reference to another step. */
20
+ flowTitle: 60,
21
+ flowStepId: 32,
22
+ flowRef: 60,
23
+ /** A Message zone's message, or the comment an Update zone leaves. */
24
+ flowMessage: 1_000,
25
+ /** Who decides a "Person decides" zone (a sign-in name), and the AI teammate who works a zone, as written. */
26
+ flowDecider: 64,
27
+ flowTeammate: 40,
28
+ /** A Build zone's other project: its path. */
29
+ flowRepo: 1_000,
30
+ /** A script's or teammate's answer, a choice's button, a Sort zone's answer and what it means, its question, and a score's level. */
31
+ flowAnswer: 40,
32
+ flowChoice: 40,
33
+ flowSortMeans: 200,
34
+ flowSortQuestion: 300,
35
+ flowSortLevel: 120,
36
+ /** A Web request zone's address, one header's value and the body it sends (an email's body too). */
37
+ flowUrl: 2_000,
38
+ flowHeader: 500,
39
+ flowBody: 8_000,
40
+ /** An email's recipients and subject. */
41
+ flowEmailTo: 500,
42
+ flowEmailSubject: 200,
43
+ /** A tool zone's tool (MCP server), its function, and the arguments as JSON. */
44
+ flowToolServer: 64,
45
+ flowToolName: 100,
46
+ flowToolArgs: 4_000,
47
+ /** A wait or reminder said in words ("3 days"), and a time zone's name. */
48
+ flowDuration: 40,
49
+ flowTimeZone: 60,
50
+ /** A flow's name, and a flow file's about line, needs, parameter questions and answers, and scripts. */
51
+ flowName: 80,
52
+ flowFileAbout: 600,
53
+ flowFileNeed: 80,
54
+ flowParameter: 200,
55
+ flowScriptAbout: 160,
56
+ flowScriptBody: 20_000,
57
+ flowScriptFile: 200,
58
+ /** A trigger's settings: a button's label and questions, a schedule and the card it makes, GitHub, Linear, webhook and email terms. */
59
+ triggerButton: 40,
60
+ triggerQuestion: 80,
61
+ triggerSchedule: 80,
62
+ triggerTitle: 200,
63
+ triggerDescription: 2_000,
64
+ triggerGithubRepo: 140,
65
+ triggerLabel: 50,
66
+ triggerBranch: 100,
67
+ triggerTeam: 12,
68
+ triggerState: 40,
69
+ triggerWebhookTitle: 120,
70
+ triggerWebhookField: 80,
71
+ triggerFolder: 100,
72
+ triggerSender: 300,
73
+ triggerSubject: 100,
74
+ /** A schedule's secrets written as one line ("API_KEY, CRM_TOKEN"). */
75
+ triggerSecrets: 500,
76
+ /** A card the lead adds to a flow: its title and description; a script it saves inline. */
77
+ flowCardTitle: 200,
78
+ flowCardDescription: 4_000,
79
+ flowScriptInline: 1_600,
19
80
  /** A research report's summary, in UTF-8 bytes. */
20
81
  reportSummary: 2_500,
82
+ /** A scout's whole report file (src/contracts/scout-report.ts), in UTF-8 bytes. */
83
+ reportPayloadBytes: 96 * 1024,
84
+ /** A report's document (markdown), in UTF-8 bytes. */
85
+ reportDocumentBytes: 64 * 1024,
86
+ /** A report's title, and a follow-up's or an item's title, in UTF-8 bytes. */
87
+ reportTitleBytes: 200,
88
+ /** Why an item fits, in UTF-8 bytes. */
89
+ reportWhyBytes: 1_000,
90
+ /** A web address a report cites, in UTF-8 bytes. */
91
+ reportUrlBytes: 2_000,
92
+ /** A screenshot's caption and its file name, in UTF-8 bytes. */
93
+ reportCaptionBytes: 300,
21
94
  /** What one flow step passes on to the steps after it. */
22
95
  stageOutput: 12_000,
23
96
  /** The planner's whole handoff file, in UTF-8 bytes. */
@@ -30,19 +103,159 @@ export const TEXT_LIMITS = {
30
103
  planItem: 600,
31
104
  /** One path a plan expects to touch. */
32
105
  planTouch: 200,
106
+ /** How many paths a task's scope may say it touches (a plan says at most 32 of them). A count, not a length. */
107
+ scopeTouches: 50,
108
+ /** Why a task was sized as it was, as its sealed route keeps it. */
109
+ sizeReason: 300,
33
110
  /** A planner's amendment note: why the filed contract must change. */
34
111
  planAmendment: 1_000,
35
112
  /** An acceptance criterion's id, statement and advisory how, in UTF-8 bytes. */
36
113
  acceptanceIdBytes: 40,
37
114
  acceptanceStatementBytes: 1_000,
38
115
  acceptanceHowBytes: 500,
116
+ /** A builder's whole proof file, in UTF-8 bytes. */
117
+ proofPayloadBytes: 64 * 1024,
118
+ /** A proof criterion's id, in UTF-8 bytes. */
119
+ proofCriterionIdBytes: 40,
120
+ /** How a proof criterion was met, in UTF-8 bytes. */
121
+ proofHowBytes: 500,
122
+ /** One proof line — a criterion statement, evidence ref, check command or summary, changed path, caveat, or
123
+ * screenshot path or caption — in UTF-8 bytes. */
124
+ proofLineBytes: 300,
125
+ /** The task sizing classifier's reason (src/contracts/task-sizing.ts). */
126
+ sizingReason: 160,
127
+ /** A review comment's or build-review finding's path, and its note or failure scenario (src/contracts/review-findings.ts). */
128
+ reviewPath: 300,
129
+ reviewNote: 500,
130
+ /** A parked decision (src/contracts/decision.ts): its recap and question, an option's id, label and consequence, and who it is for. */
131
+ decisionRecap: 2_000,
132
+ decisionQuestion: 2_000,
133
+ decisionOptionId: 40,
134
+ /** A decision's recommendation: an option's id, optionally followed by ": " and why. */
135
+ decisionRecommendation: 500,
136
+ decisionLabel: 120,
137
+ decisionConsequence: 500,
138
+ decisionAssignee: 120,
139
+ /** A teammate's turn (src/contracts/teammate-turn.ts): the answer it picks (and each option it offers), its question, its reason and what it remembers. */
140
+ teammateAnswer: 60,
141
+ teammateQuestion: 600,
142
+ teammateReason: 400,
143
+ teammateRemember: 300,
144
+ /** The lead's tools and the MCP gateway (src/contracts/lead-tools.ts, gateway-tools.ts): a task's id as a tool names
145
+ * it, a task's title and one path it touches, and a project's path as the gateway names it (an assignment brief or
146
+ * project context, and the rest of the gateway). */
147
+ taskRef: 64,
148
+ taskTitle: 200,
149
+ taskTouch: 200,
150
+ projectPath: 4_096,
151
+ gatewayRepo: 800,
152
+ /** A project-context query, a check-log search, a memory search, a task search and a changed file's path. */
153
+ contextQuery: 1_000,
154
+ logSearch: 120,
155
+ memorySearch: 300,
156
+ taskSearch: 200,
157
+ diffPath: 300,
158
+ /** A hold's or cancel's reason, an answer's rationale and option, a worker's name, an agent change's reason, a
159
+ * provider and model name, how a task's checks were asked for, and an ISO time a recap counts from. */
160
+ proposalReason: 200,
161
+ answerRationale: 400,
162
+ answerOption: 64,
163
+ workerName: 60,
164
+ agentWhy: 400,
165
+ agentProvider: 20,
166
+ agentModel: 120,
167
+ checksWords: 80,
168
+ recapSince: 30,
169
+ /** A shared action's fields as the lead proposes them (propose_action). */
170
+ actionSample: 800,
171
+ actionContent: 12_000,
172
+ actionTitle: 120,
173
+ actionName: 40,
174
+ actionCommand: 400,
175
+ actionUrl: 500,
176
+ actionSecret: 64,
177
+ actionAbout: 240,
178
+ /** A teammate change (propose_teammate): a soul section's title, a routine's schedule, a tool, an action, a limit's
179
+ * field, a name, a soul file, a note, a choice and text. */
180
+ teammateSection: 60,
181
+ teammateSchedule: 80,
182
+ teammateTool: 40,
183
+ teammateAction: 64,
184
+ teammateName: 40,
185
+ teammateSoul: 12_000,
186
+ teammateNote: 300,
187
+ teammateChoice: 60,
188
+ teammateText: 2_000,
189
+ /** A project knowledge reference id, a promise and why it is released, what the lead remembers (its text, a
190
+ * decision's reason and source), a person's name, and a question to the owner with its options. */
191
+ knowledgeReference: 20,
192
+ promise: 240,
193
+ rememberText: 240,
194
+ rememberWhy: 2_000,
195
+ rememberSource: 200,
196
+ personName: 80,
197
+ ownerQuestion: 200,
198
+ ownerOption: 40,
199
+ /** A gateway filing's idempotency key. */
200
+ idempotencyKey: 64,
201
+ /** The lead's catch-up bundle (src/contracts/lead-context.ts), in UTF-8 bytes, and the lines it clips: an open promise,
202
+ * a confirmed correction, the end of changed instructions a correction shows, and what an open proposal is about. */
203
+ leadContextBytes: 8_000,
204
+ leadPromise: 160,
205
+ leadCorrection: 300,
206
+ leadCorrectionInstructions: 240,
207
+ leadProposalAbout: 120,
208
+ /** Project knowledge (src/contracts/project-knowledge.ts), in UTF-8 bytes: the instructions (also the memory pass's
209
+ * budget for them), a reference's title, path and text, the context a run gets, the decision lines in it, and the
210
+ * repository excerpts added when at least the minimum is left. */
211
+ knowledgeInstructionsBytes: 4_000,
212
+ knowledgeTitleBytes: 120,
213
+ knowledgePathBytes: 300,
214
+ knowledgeReferenceBytes: 12_000,
215
+ knowledgeContextBytes: 24_000,
216
+ knowledgeDecisionsBytes: 1_500,
217
+ knowledgeRepositoryBytes: 6_000,
218
+ knowledgeRepositoryMinBytes: 3_000,
219
+ /** A project decision (src/contracts/project-memory.ts), in UTF-8 bytes: the choice, why, who decided, where it came
220
+ * from, and why it was retired. */
221
+ decisionClaimBytes: 240,
222
+ decisionWhyBytes: 2_000,
223
+ decisionByBytes: 80,
224
+ decisionSourceBytes: 200,
225
+ decisionRetireBytes: 500,
226
+ /** A skill package (src/contracts/project-skills.ts): its name, description, requirements and source in UTF-8 bytes,
227
+ * a file's path, SKILL.md, one file, the package and the enabled selection in bytes; a test's sample request and
228
+ * feedback in characters and bytes. */
229
+ skillNameBytes: 64,
230
+ skillDescriptionBytes: 1_024,
231
+ skillRequirementsBytes: 500,
232
+ skillSourceBytes: 500,
233
+ skillPath: 240,
234
+ skillBodyBytes: 24 * 1024,
235
+ skillFileBytes: 256 * 1024,
236
+ skillPackageBytes: 1024 * 1024,
237
+ skillSelectionBytes: 2 * 1024 * 1024,
238
+ skillSample: 800,
239
+ skillSampleBytes: 4_000,
240
+ skillFeedback: 500,
241
+ skillFeedbackBytes: 2_000,
242
+ /** The memory pass (src/contracts/memory-pass.ts): one session's trace, and in its verdict an effect, a mistake and
243
+ * the instruction it proposes. */
244
+ memoryTrace: 40_000,
245
+ memoryEffect: 300,
246
+ memoryMistake: 300,
247
+ memoryInstruction: 240,
248
+ /** A Telegram button's callback_data, in UTF-8 bytes: Telegram refuses a button with more. */
249
+ telegramCallbackDataBytes: 64,
39
250
  };
40
251
  /** One chat message on each platform. Longer text is split across messages, never cut. Teams: the text one Adaptive Card
41
252
  * holds, well inside its 28 KB message. */
42
253
  export const PLATFORM_LIMITS = { telegram: 4_096, discord: 2_000, slack: 4_000, teams: 6_000 };
43
254
  const count = (n) => n.toLocaleString("en-US");
44
255
  /** The line a model reads before it writes a field with a limit. */
45
- export function limitRule(what, limit, unit = "characters") {
256
+ export function limitRule(what, limit, unit = "characters", shorten = true) {
257
+ if (!shorten)
258
+ return `${what}: at most ${count(limit)} ${unit}. Longer text is refused.`;
46
259
  return `${what}: at most ${count(limit)} ${unit}. Put the most important part first. Longer text is not cut: you will be asked to shorten it.`;
47
260
  }
48
261
  /** How long a text is in a limit's unit. */
@@ -1,5 +1,5 @@
1
1
  import { type FoundTool, type ProjectTool, type ToolSpec } from "./project-tools.js";
2
- export declare const TOOLS_CSS = ".tools{max-width:780px;min-width:0;overflow-wrap:anywhere}.tools .card{padding:16px 18px;margin:12px 0;min-width:0}.tools .card h2{font-size:1.05rem;margin:0;color:var(--so-ink);font-weight:600}.tools .tool-heading{display:flex;align-items:center;gap:10px}.tools .tool-heading h2{flex:1;min-width:0}.tools .tool-state{font-size:.85rem;flex-shrink:0;color:var(--so-muted)}.tools .tool-state[data-ready=\"false\"]{color:var(--so-warning)}.tools .card>p{margin:6px 0 0}.tools code{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;font-size:.85rem}.tools form{display:grid;gap:10px;margin:0}.tools label{display:grid;gap:6px}.tools input,.tools select{box-sizing:border-box;width:100%;max-width:100%;min-width:0}.tools button{justify-self:start;white-space:nowrap;min-height:44px;max-width:100%;overflow:hidden;text-overflow:ellipsis}.tools details:not(.card){margin:6px 0 0;padding:0;border:0;border-radius:0;background:none;box-shadow:none}.tools details:not(.card)>summary{min-height:36px;display:flex;align-items:center;font-size:.9rem;color:var(--so-muted);cursor:pointer}.tools details:not(.card)[open]>summary{color:var(--so-ink)}.tools details.card>summary{cursor:pointer;min-height:44px;display:flex;align-items:center;font-weight:600}.tools summary{list-style:none;gap:10px}.tools summary::-webkit-details-marker{display:none}.tools summary::before{content:\"\";flex-shrink:0;width:6px;height:6px;margin:0 2px;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:rotate(-45deg);transition:transform .15s}.tools details[open]>summary::before{transform:rotate(45deg)}.tools details:not(.card) form{padding:4px 0 10px}.tools .tool-facts{margin:0 0 8px;display:grid;gap:4px;font-size:.9rem}.tools .tool-facts p{margin:0}.tools .tool-controls{display:flex;gap:12px;flex-wrap:wrap;margin-top:12px}.tools .tool-found{display:flex;gap:10px;align-items:flex-start}.tools .tool-found input{flex:0 0 auto;width:18px;height:18px;margin-top:3px}.tools .tool-found span{min-width:0}.tools .problem{padding:10px 12px;margin:8px 0 0}.tools .tool-connect{scroll-margin-top:72px}.tools .connect-wanted{background:var(--so-accent);color:var(--so-on-accent);border-color:var(--so-accent)}.tools .connect-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:8px}.tools .connect-tile{display:grid;grid-template-columns:auto 1fr;column-gap:10px;align-items:center;justify-self:stretch;text-align:left;white-space:normal;padding:10px 12px;border:1px solid var(--so-line);border-radius:10px;background:var(--so-paper);color:var(--so-ink);min-height:60px;font:inherit;cursor:pointer;scroll-margin-top:96px}.tools .connect-tile:hover{border-color:var(--so-input-line);background:var(--so-raised)}.tools .connect-tile:target,.tools .connect-tile:focus-visible{outline:2px solid var(--so-accent);outline-offset:2px}.tools .connect-tile .brand-mark{grid-row:span 2}.tools .connect-tile strong{font-weight:600;line-height:1.3}.tools .connect-tile>span:not(.brand-mark){font-size:.8rem;color:var(--so-muted);line-height:1.35;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.tools .connect-tile[data-state=\"connected\"]>span:not(.brand-mark){color:var(--so-success)}.tools .connect-local{grid-column:1/-1}.tools .connect-local>span:not(.brand-mark){-webkit-line-clamp:unset}.tools .connect-problem{margin:0}.tools .connect-problem:focus{outline:2px solid var(--so-accent);outline-offset:2px}.tools .connect-tile:disabled{cursor:default;opacity:.6}@media(max-width:600px){.tools .connect-grid{grid-template-columns:1fr 1fr}.tools .connect-tile:not(.connect-local)>span:not(.brand-mark){display:none}.tools .connect-local>span:not(.brand-mark){grid-column:2}.tools .connect-tile{min-height:52px;padding:8px 10px}.tools .connect-tile .brand-mark{grid-row:auto}.tools input,.tools select{font-size:16px}.tools .card{padding:14px}}";
2
+ export declare const TOOLS_CSS = ".tools{max-width:780px;min-width:0;overflow-wrap:anywhere}.tools .card{padding:16px 18px;margin:12px 0;min-width:0}.tools .card h2{font-size:1.05rem;margin:0;color:var(--so-ink);font-weight:600}.tools .tool-heading{display:flex;align-items:center;gap:10px}.tools .tool-heading h2{flex:1;min-width:0}.tools .tool-state{font-size:.85rem;flex-shrink:0;color:var(--so-muted)}.tools .tool-state[data-ready=\"false\"]{color:var(--so-warning)}.tools .card>p{margin:6px 0 0}.tools .tool-note{color:var(--so-warning)}.tools code{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;font-size:.85rem}.tools form{display:grid;gap:10px;margin:0}.tools label{display:grid;gap:6px}.tools input,.tools select{box-sizing:border-box;width:100%;max-width:100%;min-width:0}.tools button{justify-self:start;white-space:nowrap;min-height:44px;max-width:100%;overflow:hidden;text-overflow:ellipsis}.tools details:not(.card){margin:6px 0 0;padding:0;border:0;border-radius:0;background:none;box-shadow:none}.tools details:not(.card)>summary{min-height:36px;display:flex;align-items:center;font-size:.9rem;color:var(--so-muted);cursor:pointer}.tools details:not(.card)[open]>summary{color:var(--so-ink)}.tools details.card>summary{cursor:pointer;min-height:44px;display:flex;align-items:center;font-weight:600}.tools summary{list-style:none;gap:10px}.tools summary::-webkit-details-marker{display:none}.tools summary::before{content:\"\";flex-shrink:0;width:6px;height:6px;margin:0 2px;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:rotate(-45deg);transition:transform .15s}.tools details[open]>summary::before{transform:rotate(45deg)}.tools details:not(.card) form{padding:4px 0 10px}.tools .tool-facts{margin:0 0 8px;display:grid;gap:4px;font-size:.9rem}.tools .tool-facts p{margin:0}.tools .tool-controls{display:flex;gap:12px;flex-wrap:wrap;margin-top:12px}.tools .tool-found{display:flex;gap:10px;align-items:flex-start}.tools .tool-found input{flex:0 0 auto;width:18px;height:18px;margin-top:3px}.tools .tool-found span{min-width:0}.tools .problem{padding:10px 12px;margin:8px 0 0}.tools .tool-connect{scroll-margin-top:72px}.tools .connect-wanted{background:var(--so-accent);color:var(--so-on-accent);border-color:var(--so-accent)}.tools .connect-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:8px}.tools .connect-tile{display:grid;grid-template-columns:auto 1fr;column-gap:10px;align-items:center;justify-self:stretch;text-align:left;white-space:normal;padding:10px 12px;border:1px solid var(--so-line);border-radius:10px;background:var(--so-paper);color:var(--so-ink);min-height:60px;font:inherit;cursor:pointer;scroll-margin-top:96px}.tools .connect-tile:hover{border-color:var(--so-input-line);background:var(--so-raised)}.tools .connect-tile:target,.tools .connect-tile:focus-visible{outline:2px solid var(--so-accent);outline-offset:2px}.tools .connect-tile .brand-mark{grid-row:span 2}.tools .connect-tile strong{font-weight:600;line-height:1.3}.tools .connect-tile>span:not(.brand-mark){font-size:.8rem;color:var(--so-muted);line-height:1.35;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.tools .connect-tile[data-state=\"connected\"]>span:not(.brand-mark){color:var(--so-success)}.tools .connect-local{grid-column:1/-1}.tools .connect-local>span:not(.brand-mark){-webkit-line-clamp:unset}.tools .connect-problem{margin:0}.tools .connect-problem:focus{outline:2px solid var(--so-accent);outline-offset:2px}.tools .connect-tile:disabled{cursor:default;opacity:.6}@media(max-width:600px){.tools .connect-grid{grid-template-columns:1fr 1fr}.tools .connect-tile:not(.connect-local)>span:not(.brand-mark){display:none}.tools .connect-local>span:not(.brand-mark){grid-column:2}.tools .connect-tile{min-height:52px;padding:8px 10px}.tools .connect-tile .brand-mark{grid-row:auto}.tools input,.tools select{font-size:16px}.tools .card{padding:14px}}";
3
3
  export type ToolsView = {
4
4
  repo: string;
5
5
  project: string;
@@ -10,6 +10,7 @@ export type ToolsView = {
10
10
  about: string;
11
11
  state: "connected" | "open" | "taken";
12
12
  local?: true;
13
+ research?: string;
13
14
  }[];
14
15
  kit: string | null;
15
16
  /** The service a link asked for (a kit's checklist, the lead): its own button under the password. */
package/dist/tools-ui.js CHANGED
@@ -4,7 +4,7 @@ import { brandMarkHtml, TILE_PHONE } from "./brand-mark.js";
4
4
  import { toolCommandLine, toolStanding } from "./project-tools.js";
5
5
  const e = (value) => String(value ?? "").replace(/[&<>"']/g, c => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
6
6
  const hidden = (data) => Object.entries(data).map(([k, v]) => `<input type="hidden" name="${e(k)}" value="${e(v)}">`).join("");
7
- export const TOOLS_CSS = `.tools{max-width:780px;min-width:0;overflow-wrap:anywhere}.tools .card{padding:16px 18px;margin:12px 0;min-width:0}.tools .card h2{font-size:1.05rem;margin:0;color:var(--so-ink);font-weight:600}.tools .tool-heading{display:flex;align-items:center;gap:10px}.tools .tool-heading h2{flex:1;min-width:0}.tools .tool-state{font-size:.85rem;flex-shrink:0;color:var(--so-muted)}.tools .tool-state[data-ready="false"]{color:var(--so-warning)}.tools .card>p{margin:6px 0 0}.tools code{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;font-size:.85rem}.tools form{display:grid;gap:10px;margin:0}.tools label{display:grid;gap:6px}.tools input,.tools select{box-sizing:border-box;width:100%;max-width:100%;min-width:0}.tools button{justify-self:start;white-space:nowrap;min-height:44px;max-width:100%;overflow:hidden;text-overflow:ellipsis}.tools details:not(.card){margin:6px 0 0;padding:0;border:0;border-radius:0;background:none;box-shadow:none}.tools details:not(.card)>summary{min-height:36px;display:flex;align-items:center;font-size:.9rem;color:var(--so-muted);cursor:pointer}.tools details:not(.card)[open]>summary{color:var(--so-ink)}.tools details.card>summary{cursor:pointer;min-height:44px;display:flex;align-items:center;font-weight:600}.tools summary{list-style:none;gap:10px}.tools summary::-webkit-details-marker{display:none}.tools summary::before{content:"";flex-shrink:0;width:6px;height:6px;margin:0 2px;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:rotate(-45deg);transition:transform .15s}.tools details[open]>summary::before{transform:rotate(45deg)}.tools details:not(.card) form{padding:4px 0 10px}.tools .tool-facts{margin:0 0 8px;display:grid;gap:4px;font-size:.9rem}.tools .tool-facts p{margin:0}.tools .tool-controls{display:flex;gap:12px;flex-wrap:wrap;margin-top:12px}.tools .tool-found{display:flex;gap:10px;align-items:flex-start}.tools .tool-found input{flex:0 0 auto;width:18px;height:18px;margin-top:3px}.tools .tool-found span{min-width:0}.tools .problem{padding:10px 12px;margin:8px 0 0}.tools .tool-connect{scroll-margin-top:72px}.tools .connect-wanted{background:var(--so-accent);color:var(--so-on-accent);border-color:var(--so-accent)}.tools .connect-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:8px}.tools .connect-tile{display:grid;grid-template-columns:auto 1fr;column-gap:10px;align-items:center;justify-self:stretch;text-align:left;white-space:normal;padding:10px 12px;border:1px solid var(--so-line);border-radius:10px;background:var(--so-paper);color:var(--so-ink);min-height:60px;font:inherit;cursor:pointer;scroll-margin-top:96px}.tools .connect-tile:hover{border-color:var(--so-input-line);background:var(--so-raised)}.tools .connect-tile:target,.tools .connect-tile:focus-visible{outline:2px solid var(--so-accent);outline-offset:2px}.tools .connect-tile .brand-mark{grid-row:span 2}.tools .connect-tile strong{font-weight:600;line-height:1.3}.tools .connect-tile>span:not(.brand-mark){font-size:.8rem;color:var(--so-muted);line-height:1.35;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.tools .connect-tile[data-state="connected"]>span:not(.brand-mark){color:var(--so-success)}.tools .connect-local{grid-column:1/-1}.tools .connect-local>span:not(.brand-mark){-webkit-line-clamp:unset}.tools .connect-problem{margin:0}.tools .connect-problem:focus{outline:2px solid var(--so-accent);outline-offset:2px}.tools .connect-tile:disabled{cursor:default;opacity:.6}${TILE_PHONE}{.tools .connect-grid{grid-template-columns:1fr 1fr}.tools .connect-tile:not(.connect-local)>span:not(.brand-mark){display:none}.tools .connect-local>span:not(.brand-mark){grid-column:2}.tools .connect-tile{min-height:52px;padding:8px 10px}.tools .connect-tile .brand-mark{grid-row:auto}.tools input,.tools select{font-size:16px}.tools .card{padding:14px}}`;
7
+ export const TOOLS_CSS = `.tools{max-width:780px;min-width:0;overflow-wrap:anywhere}.tools .card{padding:16px 18px;margin:12px 0;min-width:0}.tools .card h2{font-size:1.05rem;margin:0;color:var(--so-ink);font-weight:600}.tools .tool-heading{display:flex;align-items:center;gap:10px}.tools .tool-heading h2{flex:1;min-width:0}.tools .tool-state{font-size:.85rem;flex-shrink:0;color:var(--so-muted)}.tools .tool-state[data-ready="false"]{color:var(--so-warning)}.tools .card>p{margin:6px 0 0}.tools .tool-note{color:var(--so-warning)}.tools code{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;font-size:.85rem}.tools form{display:grid;gap:10px;margin:0}.tools label{display:grid;gap:6px}.tools input,.tools select{box-sizing:border-box;width:100%;max-width:100%;min-width:0}.tools button{justify-self:start;white-space:nowrap;min-height:44px;max-width:100%;overflow:hidden;text-overflow:ellipsis}.tools details:not(.card){margin:6px 0 0;padding:0;border:0;border-radius:0;background:none;box-shadow:none}.tools details:not(.card)>summary{min-height:36px;display:flex;align-items:center;font-size:.9rem;color:var(--so-muted);cursor:pointer}.tools details:not(.card)[open]>summary{color:var(--so-ink)}.tools details.card>summary{cursor:pointer;min-height:44px;display:flex;align-items:center;font-weight:600}.tools summary{list-style:none;gap:10px}.tools summary::-webkit-details-marker{display:none}.tools summary::before{content:"";flex-shrink:0;width:6px;height:6px;margin:0 2px;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:rotate(-45deg);transition:transform .15s}.tools details[open]>summary::before{transform:rotate(45deg)}.tools details:not(.card) form{padding:4px 0 10px}.tools .tool-facts{margin:0 0 8px;display:grid;gap:4px;font-size:.9rem}.tools .tool-facts p{margin:0}.tools .tool-controls{display:flex;gap:12px;flex-wrap:wrap;margin-top:12px}.tools .tool-found{display:flex;gap:10px;align-items:flex-start}.tools .tool-found input{flex:0 0 auto;width:18px;height:18px;margin-top:3px}.tools .tool-found span{min-width:0}.tools .problem{padding:10px 12px;margin:8px 0 0}.tools .tool-connect{scroll-margin-top:72px}.tools .connect-wanted{background:var(--so-accent);color:var(--so-on-accent);border-color:var(--so-accent)}.tools .connect-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:8px}.tools .connect-tile{display:grid;grid-template-columns:auto 1fr;column-gap:10px;align-items:center;justify-self:stretch;text-align:left;white-space:normal;padding:10px 12px;border:1px solid var(--so-line);border-radius:10px;background:var(--so-paper);color:var(--so-ink);min-height:60px;font:inherit;cursor:pointer;scroll-margin-top:96px}.tools .connect-tile:hover{border-color:var(--so-input-line);background:var(--so-raised)}.tools .connect-tile:target,.tools .connect-tile:focus-visible{outline:2px solid var(--so-accent);outline-offset:2px}.tools .connect-tile .brand-mark{grid-row:span 2}.tools .connect-tile strong{font-weight:600;line-height:1.3}.tools .connect-tile>span:not(.brand-mark){font-size:.8rem;color:var(--so-muted);line-height:1.35;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.tools .connect-tile[data-state="connected"]>span:not(.brand-mark){color:var(--so-success)}.tools .connect-local{grid-column:1/-1}.tools .connect-local>span:not(.brand-mark){-webkit-line-clamp:unset}.tools .connect-problem{margin:0}.tools .connect-problem:focus{outline:2px solid var(--so-accent);outline-offset:2px}.tools .connect-tile:disabled{cursor:default;opacity:.6}${TILE_PHONE}{.tools .connect-grid{grid-template-columns:1fr 1fr}.tools .connect-tile:not(.connect-local)>span:not(.brand-mark){display:none}.tools .connect-local>span:not(.brand-mark){grid-column:2}.tools .connect-tile{min-height:52px;padding:8px 10px}.tools .connect-tile .brand-mark{grid-row:auto}.tools input,.tools select{font-size:16px}.tools .card{padding:14px}}`;
8
8
  /** The project chooser: it opens the chosen project at once; without scripts, its button does. */
9
9
  export function toolsProjectPicker(projects, chosen, keep = {}) {
10
10
  if (projects.length < 2)
@@ -15,7 +15,7 @@ export function toolsProjectPicker(projects, chosen, keep = {}) {
15
15
  /** Choosing another project marks every form on the page as stale before it reloads, so a tap racing the reload is refused. */
16
16
  export const TOOLS_PROJECT_SCRIPT = `(function(){var s=document.querySelector('select[data-tools-project]');if(!s)return;s.addEventListener('change',function(){document.querySelectorAll('input[name=shown]').forEach(function(i){i.value=s.value;});});})();`;
17
17
  const password = '<label>Your Toolroll password<input type="password" name="password" autocomplete="current-password" required></label>';
18
- function toolCard(one, base, manage, project) {
18
+ function toolCard(one, base, manage, project, research = null) {
19
19
  const { tool, secretsSet } = one;
20
20
  const standing = toolStanding(tool, secretsSet);
21
21
  // One line per secret: the disclosure is both where it stands and where it is set.
@@ -26,8 +26,11 @@ function toolCard(one, base, manage, project) {
26
26
  const tested = tool.lastTest?.ok ? `<p>Tested ${e(tool.lastTest.at.slice(0, 16).replace("T", " "))} UTC: ${e(tool.lastTest.tools.join(", ") || "it lists no tools")}</p>` : "";
27
27
  const details = `<details><summary>Details</summary><div class="tool-facts"><p>Starts: <code>${e(toolCommandLine(tool.spec))}</code></p><p>From ${e(tool.source)} · added by ${e(tool.createdBy)}</p>${tested}</div></details>`;
28
28
  const failed = tool.lastTest !== null && !tool.lastTest.ok ? `<p class="problem" role="status">${e(tool.lastTest.problem ?? "The last test failed.")}</p>` : "";
29
+ // Connected, but research can't use it (its grant writes): said once, with the one way to fix it.
30
+ const reconnect = research === null || !manage ? "" : `<details><summary>Reconnect read-only</summary><form method="post" action="/settings/tools/connect">${hidden({ ...base, service: tool.name, access: "read" })}${password}<button>Reconnect read-only</button></form></details>`;
31
+ const limited = research === null ? "" : `<p class="tool-note" role="status">${e(research)}</p>${reconnect}`;
29
32
  const controls = manage ? `<div class="tool-controls"><form method="post" action="/settings/tools/change">${hidden({ ...base, action: "test", name: tool.name })}<button aria-label="Test ${e(tool.name)} in ${e(project)}">Test</button></form><form method="post" action="/settings/tools/change">${hidden({ ...base, action: "remove", name: tool.name })}<button class="secondary">Remove from ${e(project)}</button></form></div>` : "";
30
- return `<article class="card" id="tool-${e(tool.name)}"><div class="tool-heading">${brandMarkHtml(tool.source === "custom" ? null : tool.name, tool.name, standing.ready)}<h2>${e(tool.name)}</h2><span class="tool-state" data-ready="${standing.ready}">${e(standing.words)}</span></div><p>${e(tool.spec.about)}</p>${failed}${secrets}${details}${controls}</article>`;
33
+ return `<article class="card" id="tool-${e(tool.name)}"><div class="tool-heading">${brandMarkHtml(tool.source === "custom" ? null : tool.name, tool.name, standing.ready)}<h2>${e(tool.name)}</h2><span class="tool-state" data-ready="${standing.ready}">${e(standing.words)}</span></div><p>${e(tool.spec.about)}</p>${failed}${limited}${secrets}${details}${controls}</article>`;
31
34
  }
32
35
  /** A service connected here, offered to the person's other projects: each button signs in again for that project (tokens are never copied). */
33
36
  function elsewhere(view, base) {
@@ -42,7 +45,7 @@ export function toolsHtml(view, csrf, canManage, message = {}) {
42
45
  const base = { csrf, repo: view.repo, shown: view.repo };
43
46
  const to = ` to ${e(view.project)}`;
44
47
  const intro = `<p>Builds in ${e(view.project)} use exactly these tools. MCP servers set up elsewhere on this computer aren't used.</p><details><summary>How tools work</summary><ul><li>A tool you add reaches work you approve from now on. Approve a task again to give it a new tool.</li><li>Removing a tool takes it away from every build right away.</li><li>Secrets stay on this computer, are never shown again, and go only to the tool that needs them.</li><li>Reviewers and the lead chat never get tools. Gemini builds don't use tools yet.</li></ul></details>`;
45
- const cards = view.tools.length === 0 ? '<p class="meta">No tools yet. Builds in this project get none.</p>' : view.tools.map(one => toolCard(one, base, manage, view.project)).join("");
48
+ const cards = view.tools.length === 0 ? '<p class="meta">No tools yet. Builds in this project get none.</p>' : view.tools.map(one => toolCard(one, base, manage, view.project, view.connections.find(c => c.id === one.tool.name && c.state === "connected")?.research ?? null)).join("");
46
49
  const fromList = view.catalog.length === 0 ? "" : `<form method="post" action="/settings/tools/change">${hidden({ ...base, action: "add-catalog" })}<label>Common tools<select name="catalog">${view.catalog.map(one => `<option value="${e(one.name)}">${e(one.label)}: ${e(one.about)}</option>`).join("")}</select></label>${password}<button>Add${to}</button></form>`;
47
50
  const custom = `<details><summary>Add your own</summary><form method="post" action="/settings/tools/change">${hidden({ ...base, action: "add-custom" })}<label>Name<input name="name" pattern="[a-z0-9][a-z0-9_\\-]{0,39}" maxlength="40" required placeholder="for example: postgres"></label><label>It runs as<select name="transport"><option value="stdio">A program on this computer</option><option value="http">A web address</option></select></label><label>Command or address<input name="target" required placeholder="npx -y some-mcp-server@1.2.3, or https://…"></label><label>Secrets it needs (names, comma separated)<input name="secrets" placeholder="for example: DATABASE_URL"></label><p class="meta">For a web address, the first secret is sent as its sign-in token. Set each secret's value after adding.</p>${password}<button>Add${to}</button></form></details>`;
48
51
  // One click: sign in on the service's own page. The password is the same as for adding any tool.