toolroll 0.9.36 → 0.9.38

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 (175) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/dist/assignment-adapters.d.ts +12 -48
  3. package/dist/assignment-adapters.js +33 -43
  4. package/dist/browser/THIRD_PARTY_NOTICES.txt +3 -3
  5. package/dist/browser/workspace.css +1 -1
  6. package/dist/browser/workspace.js +26 -26
  7. package/dist/browser-crew.js +5 -0
  8. package/dist/browser-workspace.d.ts +2 -0
  9. package/dist/build-review.d.ts +1 -1
  10. package/dist/build-review.js +2 -1
  11. package/dist/chat-acceptance.d.ts +1 -1
  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-channel.js +20 -9
  16. package/dist/chat-decide.d.ts +18 -4
  17. package/dist/chat-decide.js +31 -11
  18. package/dist/chat-delivery-state.d.ts +12 -70
  19. package/dist/chat-delivery-state.js +17 -135
  20. package/dist/chat-delivery.js +58 -30
  21. package/dist/chat-flow.js +8 -6
  22. package/dist/chat-inbox.js +1 -1
  23. package/dist/coding-context.d.ts +2 -39
  24. package/dist/coding-context.js +2 -1
  25. package/dist/coding-handoff.d.ts +6 -25
  26. package/dist/coding-handoff.js +11 -5
  27. package/dist/coding-types.d.ts +2 -26
  28. package/dist/coding-workspace.d.ts +2 -0
  29. package/dist/coding-workspace.js +50 -14
  30. package/dist/contracts/acceptance-terms.d.ts +14 -0
  31. package/dist/contracts/acceptance-terms.js +16 -0
  32. package/dist/contracts/chat-actions.d.ts +328 -0
  33. package/dist/contracts/chat-actions.js +133 -0
  34. package/dist/contracts/chat-callback-rows.d.ts +59 -0
  35. package/dist/contracts/chat-callback-rows.js +46 -0
  36. package/dist/contracts/chat-content.d.ts +276 -0
  37. package/dist/contracts/chat-content.js +127 -0
  38. package/dist/contracts/chat-tables.d.ts +6 -0
  39. package/dist/contracts/chat-tables.js +136 -0
  40. package/dist/contracts/coding-context.d.ts +97 -0
  41. package/dist/contracts/coding-context.js +60 -0
  42. package/dist/contracts/coding-handoff.d.ts +44 -0
  43. package/dist/contracts/coding-handoff.js +47 -0
  44. package/dist/contracts/coding-workspace.d.ts +165 -0
  45. package/dist/contracts/coding-workspace.js +71 -0
  46. package/dist/contracts/contract.d.ts +7 -2
  47. package/dist/contracts/contract.js +21 -12
  48. package/dist/contracts/decision.d.ts +45 -0
  49. package/dist/contracts/decision.js +45 -0
  50. package/dist/contracts/discord-callback.d.ts +45 -0
  51. package/dist/contracts/discord-callback.js +34 -0
  52. package/dist/contracts/flow-card.d.ts +50 -0
  53. package/dist/contracts/flow-card.js +46 -0
  54. package/dist/contracts/flow-send.d.ts +119 -0
  55. package/dist/contracts/flow-send.js +112 -0
  56. package/dist/contracts/flow.d.ts +5 -0
  57. package/dist/contracts/flow.js +5 -0
  58. package/dist/contracts/gateway-tools.d.ts +374 -0
  59. package/dist/contracts/gateway-tools.js +94 -0
  60. package/dist/contracts/lead-context.d.ts +165 -0
  61. package/dist/contracts/lead-context.js +73 -0
  62. package/dist/contracts/lead-tools.d.ts +1905 -0
  63. package/dist/contracts/lead-tools.js +265 -0
  64. package/dist/contracts/memory-pass.d.ts +158 -0
  65. package/dist/contracts/memory-pass.js +102 -0
  66. package/dist/contracts/project-knowledge.d.ts +126 -0
  67. package/dist/contracts/project-knowledge.js +93 -0
  68. package/dist/contracts/project-memory.d.ts +117 -0
  69. package/dist/contracts/project-memory.js +51 -0
  70. package/dist/contracts/project-skills.d.ts +121 -0
  71. package/dist/contracts/project-skills.js +76 -0
  72. package/dist/contracts/review-findings.d.ts +46 -0
  73. package/dist/contracts/review-findings.js +48 -0
  74. package/dist/contracts/route.d.ts +484 -0
  75. package/dist/contracts/route.js +128 -0
  76. package/dist/contracts/scope.d.ts +105 -0
  77. package/dist/contracts/scope.js +114 -0
  78. package/dist/contracts/scout-report.d.ts +234 -0
  79. package/dist/contracts/scout-report.js +141 -0
  80. package/dist/contracts/slack-callback.d.ts +43 -0
  81. package/dist/contracts/slack-callback.js +35 -0
  82. package/dist/contracts/sort-answer.d.ts +83 -0
  83. package/dist/contracts/sort-answer.js +77 -0
  84. package/dist/contracts/stage-output.d.ts +127 -0
  85. package/dist/contracts/stage-output.js +166 -0
  86. package/dist/contracts/task-sizing.d.ts +24 -0
  87. package/dist/contracts/task-sizing.js +28 -0
  88. package/dist/contracts/teammate-turn.d.ts +43 -0
  89. package/dist/contracts/teammate-turn.js +51 -0
  90. package/dist/contracts/teams-callback.d.ts +14 -0
  91. package/dist/contracts/teams-callback.js +14 -0
  92. package/dist/contracts/telegram-callback.d.ts +120 -0
  93. package/dist/contracts/telegram-callback.js +97 -0
  94. package/dist/decision.d.ts +11 -20
  95. package/dist/decision.js +86 -167
  96. package/dist/desktop-update.d.ts +1 -1
  97. package/dist/discord-chat.js +31 -20
  98. package/dist/flow-engine.js +14 -17
  99. package/dist/flow-items.d.ts +4 -13
  100. package/dist/flow-items.js +1 -15
  101. package/dist/flow-send.d.ts +6 -37
  102. package/dist/flow-send.js +18 -14
  103. package/dist/flow-share.js +7 -1
  104. package/dist/flow-sort.d.ts +7 -26
  105. package/dist/flow-sort.js +25 -20
  106. package/dist/flow-steps.js +3 -2
  107. package/dist/flows.d.ts +2 -4
  108. package/dist/flows.js +9 -5
  109. package/dist/invoke.js +4 -4
  110. package/dist/lead-context.d.ts +4 -2
  111. package/dist/lead-context.js +24 -17
  112. package/dist/mate-progress.d.ts +26 -0
  113. package/dist/mate-progress.js +24 -0
  114. package/dist/mate-tools.d.ts +4 -0
  115. package/dist/mate-tools.js +108 -275
  116. package/dist/mate.js +11 -2
  117. package/dist/mcp-connect.d.ts +24 -4
  118. package/dist/mcp-connect.js +78 -15
  119. package/dist/mcp.js +84 -216
  120. package/dist/memory-pass.d.ts +17 -42
  121. package/dist/memory-pass.js +54 -26
  122. package/dist/phase-routing.d.ts +19 -72
  123. package/dist/phase-routing.js +34 -164
  124. package/dist/project-knowledge.d.ts +16 -35
  125. package/dist/project-knowledge.js +42 -21
  126. package/dist/project-memory.d.ts +2 -28
  127. package/dist/project-memory.js +28 -13
  128. package/dist/project-skills.d.ts +16 -38
  129. package/dist/project-skills.js +41 -32
  130. package/dist/project-tools.d.ts +1 -0
  131. package/dist/project-tools.js +4 -4
  132. package/dist/provider.d.ts +2 -0
  133. package/dist/provider.js +4 -16
  134. package/dist/result-shots.d.ts +1 -1
  135. package/dist/result-shots.js +2 -1
  136. package/dist/review-switch.d.ts +3 -8
  137. package/dist/reviewer.d.ts +8 -12
  138. package/dist/reviewer.js +11 -35
  139. package/dist/scope.d.ts +21 -43
  140. package/dist/scope.js +42 -128
  141. package/dist/scout-report.d.ts +9 -194
  142. package/dist/scout-report.js +113 -264
  143. package/dist/scout.js +15 -11
  144. package/dist/serve.js +23 -5
  145. package/dist/setup-guide.d.ts +2 -2
  146. package/dist/slack-chat.js +26 -20
  147. package/dist/slack-state.js +2 -1
  148. package/dist/store.d.ts +9 -32
  149. package/dist/store.js +18 -15
  150. package/dist/structured-output.js +2 -1
  151. package/dist/subscription-chat.d.ts +4 -0
  152. package/dist/subscription-chat.js +4 -1
  153. package/dist/task-review-brief.d.ts +1 -7
  154. package/dist/task-review-brief.js +6 -6
  155. package/dist/task-sizing.d.ts +3 -27
  156. package/dist/task-sizing.js +11 -26
  157. package/dist/task-text.d.ts +0 -5
  158. package/dist/task-text.js +1 -3
  159. package/dist/teammate-question.js +7 -5
  160. package/dist/teammate-work.js +9 -5
  161. package/dist/teammates.d.ts +13 -64
  162. package/dist/teammates.js +32 -33
  163. package/dist/teams-chat.js +20 -11
  164. package/dist/telegram-flow.d.ts +9 -0
  165. package/dist/telegram-flow.js +44 -8
  166. package/dist/telegram-mate.d.ts +4 -14
  167. package/dist/telegram-mate.js +9 -8
  168. package/dist/telegram-team.d.ts +2 -19
  169. package/dist/telegram.d.ts +1 -1
  170. package/dist/telegram.js +94 -68
  171. package/dist/text-limits.d.ts +142 -1
  172. package/dist/text-limits.js +144 -1
  173. package/dist/tools-ui.d.ts +2 -1
  174. package/dist/tools-ui.js +7 -4
  175. package/package.json +17 -12
@@ -79,6 +79,18 @@ export const TEXT_LIMITS = {
79
79
  flowScriptInline: 1_600,
80
80
  /** A research report's summary, in UTF-8 bytes. */
81
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,
82
94
  /** What one flow step passes on to the steps after it. */
83
95
  stageOutput: 12_000,
84
96
  /** The planner's whole handoff file, in UTF-8 bytes. */
@@ -91,6 +103,10 @@ export const TEXT_LIMITS = {
91
103
  planItem: 600,
92
104
  /** One path a plan expects to touch. */
93
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,
94
110
  /** A planner's amendment note: why the filed contract must change. */
95
111
  planAmendment: 1_000,
96
112
  /** An acceptance criterion's id, statement and advisory how, in UTF-8 bytes. */
@@ -106,13 +122,140 @@ export const TEXT_LIMITS = {
106
122
  /** One proof line — a criterion statement, evidence ref, check command or summary, changed path, caveat, or
107
123
  * screenshot path or caption — in UTF-8 bytes. */
108
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,
109
250
  };
110
251
  /** One chat message on each platform. Longer text is split across messages, never cut. Teams: the text one Adaptive Card
111
252
  * holds, well inside its 28 KB message. */
112
253
  export const PLATFORM_LIMITS = { telegram: 4_096, discord: 2_000, slack: 4_000, teams: 6_000 };
113
254
  const count = (n) => n.toLocaleString("en-US");
114
255
  /** The line a model reads before it writes a field with a limit. */
115
- 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.`;
116
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.`;
117
260
  }
118
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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolroll",
3
- "version": "0.9.36",
3
+ "version": "0.9.38",
4
4
  "description": "A control plane for unattended coding agents — queue tasks, walk away, come back to pull requests. Agents build in leased worktrees, never touch your default branch, and interrupt you only for decisions that need a human.",
5
5
  "directories": {
6
6
  "doc": "docs"
@@ -64,18 +64,18 @@
64
64
  },
65
65
  "devDependencies": {
66
66
  "@tailwindcss/cli": "4.3.3",
67
- "@types/mailparser": "3.4.6",
68
- "@types/node": "26.2.0",
67
+ "@types/mailparser": "3.9.0",
68
+ "@types/node": "26.6.4",
69
69
  "@types/react": "19.3.0",
70
70
  "@types/react-dom": "19.3.0",
71
71
  "esbuild": "0.28.2",
72
- "happy-dom": "20.12.0",
72
+ "happy-dom": "20.14.5",
73
73
  "paper": "0.12.18",
74
74
  "simple-icons": "16.34.0",
75
75
  "tailwindcss": "4.3.3",
76
- "tsx": "4.23.12",
76
+ "tsx": "4.23.15",
77
77
  "typescript": "7.0.2",
78
- "vitest": "4.1.10"
78
+ "vitest": "4.1.11"
79
79
  },
80
80
  "files": [
81
81
  "dist",
@@ -93,6 +93,11 @@
93
93
  "scripts/fixtures/restart-service.mjs",
94
94
  "THIRD_PARTY_NOTICES.md"
95
95
  ],
96
+ "overrides": {
97
+ "@tailwindcss/cli@4.3.3": {
98
+ "@parcel/watcher": "2.6.0"
99
+ }
100
+ },
96
101
  "dependencies": {
97
102
  "@radix-ui/react-collapsible": "1.1.20",
98
103
  "@radix-ui/react-dialog": "1.1.23",
@@ -105,15 +110,15 @@
105
110
  "@radix-ui/react-switch": "1.3.7",
106
111
  "@radix-ui/react-tabs": "1.1.21",
107
112
  "@radix-ui/react-tooltip": "1.2.16",
108
- "@slack/socket-mode": "3.0.1",
109
- "@xyflow/react": "12.11.6",
113
+ "@slack/socket-mode": "3.1.0",
114
+ "@xyflow/react": "12.12.0",
110
115
  "class-variance-authority": "0.7.1",
111
116
  "clsx": "2.1.1",
112
117
  "discord.js": "14.27.0",
113
- "imapflow": "2.0.6",
114
- "lucide-react": "1.47.0",
115
- "mailparser": "3.9.28",
116
- "nodemailer": "10.0.10",
118
+ "imapflow": "2.2.5",
119
+ "lucide-react": "1.52.0",
120
+ "mailparser": "3.9.36",
121
+ "nodemailer": "10.0.15",
117
122
  "react": "19.3.0",
118
123
  "react-dom": "19.3.0",
119
124
  "sonner": "2.0.8",