toolroll 0.9.27 → 0.9.29

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 (70) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/agentconfig.js +71 -3
  3. package/dist/browser/workspace.js +3 -3
  4. package/dist/build-review.js +3 -2
  5. package/dist/builder.js +10 -8
  6. package/dist/chat-actions.js +7 -4
  7. package/dist/chat-flow.js +10 -6
  8. package/dist/converse.js +3 -2
  9. package/dist/coordinator-proposals.js +2 -2
  10. package/dist/decision.d.ts +3 -3
  11. package/dist/decision.js +4 -3
  12. package/dist/desktop-update.d.ts +1 -1
  13. package/dist/evidence.js +1 -1
  14. package/dist/flow-actions.d.ts +1 -0
  15. package/dist/flow-actions.js +6 -7
  16. package/dist/flow-code.d.ts +3 -2
  17. package/dist/flow-code.js +6 -4
  18. package/dist/flow-draft.d.ts +10 -4
  19. package/dist/flow-draft.js +19 -8
  20. package/dist/flow-engine.d.ts +0 -3
  21. package/dist/flow-engine.js +17 -10
  22. package/dist/flow-people.d.ts +0 -1
  23. package/dist/flow-people.js +9 -1
  24. package/dist/flow-send.js +3 -2
  25. package/dist/flow-steps.js +33 -10
  26. package/dist/flows.d.ts +23 -8
  27. package/dist/flows.js +106 -52
  28. package/dist/guides.js +1 -1
  29. package/dist/lead-context.d.ts +3 -9
  30. package/dist/lead-context.js +21 -5
  31. package/dist/lead-status.js +17 -12
  32. package/dist/mate-doors.js +8 -2
  33. package/dist/mate-tools.js +27 -10
  34. package/dist/mate.js +13 -2
  35. package/dist/needs-you.d.ts +2 -2
  36. package/dist/needs-you.js +3 -0
  37. package/dist/operate.d.ts +3 -0
  38. package/dist/operate.js +144 -9
  39. package/dist/phase-routing.d.ts +86 -5
  40. package/dist/phase-routing.js +229 -17
  41. package/dist/process-liveness.d.ts +1 -0
  42. package/dist/process-liveness.js +5 -0
  43. package/dist/project-skills.d.ts +10 -0
  44. package/dist/proposal.d.ts +10 -2
  45. package/dist/proposal.js +25 -10
  46. package/dist/recipe-ui.js +2 -1
  47. package/dist/scout-report.d.ts +10 -4
  48. package/dist/scout-report.js +23 -6
  49. package/dist/scout.d.ts +2 -0
  50. package/dist/scout.js +83 -10
  51. package/dist/serve.js +71 -15
  52. package/dist/setup-guide.d.ts +2 -2
  53. package/dist/store.d.ts +72 -8
  54. package/dist/store.js +230 -29
  55. package/dist/task-sizing.d.ts +78 -0
  56. package/dist/task-sizing.js +228 -0
  57. package/dist/task-text.d.ts +4 -4
  58. package/dist/task-text.js +10 -6
  59. package/dist/teammate-work.js +31 -7
  60. package/dist/teammates.d.ts +20 -1
  61. package/dist/teammates.js +18 -5
  62. package/dist/teams-chat.d.ts +4 -1
  63. package/dist/teams-chat.js +17 -6
  64. package/dist/telegram-flow.js +6 -2
  65. package/dist/test-temp.js +1 -1
  66. package/dist/text-limits.d.ts +67 -0
  67. package/dist/text-limits.js +73 -0
  68. package/dist/tier-report.d.ts +44 -0
  69. package/dist/tier-report.js +71 -0
  70. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.29 — 2026-10-04
4
+
5
+ - **Tasks filed from the command line get sized too.** `task scope` now sizes
6
+ the task from its goal before the plan is sealed, so work the lead files
7
+ gets the same fast-or-strong model choice as work filed in the console.
8
+ - **A reused process ID no longer blocks finishing a release.** Once
9
+ Toolroll has recorded that a process exited, a new program taking its
10
+ number doesn't count as the old one. Leftover process records close when the
11
+ run ends, and a wait that clears by itself says so instead of asking you to
12
+ confirm.
13
+ - **Status shows each task once.** A task that is building appears only under
14
+ Building, an approved plan no longer reads as waiting for review, and risky
15
+ routes say "Risky" instead of "routine".
16
+
17
+ ## 0.9.28 — 2026-10-04
18
+
19
+ - **Each task gets the right model.** When a task is filed, a fast
20
+ classifier on your own subscription sizes it as small, medium or large and
21
+ flags risky work. Small changes build on a fast model with no plan. Large or
22
+ risky ones get your strong models for planning and building. The choice is
23
+ sealed into the plan you approve, with the reason, and you can override it.
24
+ Where a tier has both Claude and Codex, it picks the one with more of its
25
+ plan left. `toolroll spend` shows what each tier took.
26
+ - **Nothing gets cut off.** Task goals hold 8,000 characters (up from 2,000),
27
+ notes 4,000 (up from 500) and flow instructions 8,000. Agents are told each
28
+ limit before they write and get one chance to shorten. Anything still over
29
+ is kept whole and linked. Chat messages split instead of cutting.
30
+
3
31
  ## 0.9.27 — 2026-10-04
4
32
 
5
33
  - **Integrations show their real logos.** Every integration has its own mark
@@ -234,6 +234,39 @@ export function resolveScopeChain(store, repo, ref, flags, baseAuthMode) {
234
234
  }
235
235
  const PHASE_NOUN = { plan: "planner", build: "builder", repair: "repair", review: "reviewer" };
236
236
  export function resolveRouteCandidates(store, repo, pins = {}) {
237
+ // The LIGHT tier (v2): the operator's named fast builder, project over
238
+ // installation, never inherited and never inferred. Only the build phase
239
+ // has one: a small change makes no plan, and repairs resume the builder.
240
+ const lightOf = (phase) => {
241
+ const project = repo === null ? null : store.phaseTierConfig(repo, phase, "light");
242
+ const row = project ?? store.phaseTierConfig(INSTALLATION_SCOPE, phase, "light");
243
+ if (row === null)
244
+ return { ok: true, light: null };
245
+ const source = project !== null ? "project (light)" : "installation (light)";
246
+ if (!isProviderId(row.provider) || row.model === null || row.model === "") {
247
+ return { ok: false, problem: `the ${source} ${PHASE_NOUN[phase]} names ${row.provider}${row.model === null || row.model === "" ? " with no model" : ` · ${row.model}`} — a light agent is an exact pair: \`config set ${phase} --tier light --provider … --model …\`, or \`--clear\` it` };
248
+ }
249
+ const valid = validateSpec({ provider: row.provider, model: row.model });
250
+ if (!valid.ok)
251
+ return { ok: false, problem: `the ${source} ${PHASE_NOUN[phase]}: ${valid.problem}` };
252
+ return { ok: true, light: { provider: row.provider, model: row.model, source } };
253
+ };
254
+ // Alternates (v2): one more candidate per provider on a tier, project over installation.
255
+ const alternatesOf = (phase, tier) => {
256
+ const project = repo === null ? [] : store.phaseTierAlternates(repo, phase, tier);
257
+ const rows = project.length > 0 ? project : store.phaseTierAlternates(INSTALLATION_SCOPE, phase, tier);
258
+ const source = `${project.length > 0 ? "project" : "installation"} (${tier}, also)`;
259
+ const alternates = [];
260
+ for (const row of rows) {
261
+ if (!isProviderId(row.provider))
262
+ return { ok: false, problem: `the ${source} ${PHASE_NOUN[phase]} names unknown provider ${row.provider} — \`config clear ${phase} --also --provider ${row.provider}\`` };
263
+ const valid = validateSpec({ provider: row.provider, model: row.model });
264
+ if (!valid.ok)
265
+ return { ok: false, problem: `the ${source} ${PHASE_NOUN[phase]}: ${valid.problem}` };
266
+ alternates.push({ provider: row.provider, model: row.model, source });
267
+ }
268
+ return { ok: true, alternates };
269
+ };
237
270
  const strongOf = (phase) => {
238
271
  const strongProject = repo === null ? null : store.phaseTierConfig(repo, phase, "strong");
239
272
  const strongInstallation = store.phaseTierConfig(INSTALLATION_SCOPE, phase, "strong");
@@ -284,7 +317,34 @@ export function resolveRouteCandidates(store, repo, pins = {}) {
284
317
  return { ok: false, phase, problem: routine.problem };
285
318
  if (!strong.ok)
286
319
  return { ok: false, phase, problem: strong.problem };
287
- out[phase] = { routine: routine.routine, strong: strong.strong };
320
+ const light = phase === "build" ? lightOf(phase) : { ok: true, light: null };
321
+ if (!light.ok)
322
+ return { ok: false, phase, problem: light.problem };
323
+ if (phase === "review") {
324
+ out.review = { routine: routine.routine, strong: strong.strong };
325
+ continue;
326
+ }
327
+ // A tier's candidates on other providers (`--also`), one per provider.
328
+ const alternates = {};
329
+ let primary = { light: light.light, strong: strong.strong };
330
+ for (const tier of (phase === "build" ? ["light", "routine", "strong"] : ["routine", "strong"])) {
331
+ const read = alternatesOf(phase, tier);
332
+ if (!read.ok)
333
+ return { ok: false, phase, problem: read.problem };
334
+ const own = tier === "routine" ? routine.routine : primary[tier];
335
+ // With no candidate of its own, a tier's first alternate stands as it.
336
+ const list = own === null ? read.alternates.slice(1) : read.alternates.filter(one => one.provider !== own.provider);
337
+ if (own === null && read.alternates[0] !== undefined)
338
+ primary = { ...primary, [tier]: read.alternates[0] };
339
+ if (list.length > 0)
340
+ alternates[tier] = list;
341
+ }
342
+ out[phase] = {
343
+ routine: routine.routine,
344
+ strong: primary.strong,
345
+ ...(primary.light === null ? {} : { light: primary.light }),
346
+ ...(Object.keys(alternates).length === 0 ? {} : { alternates }),
347
+ };
288
348
  }
289
349
  // Repair: the configured row is optional (inherit is the law when none
290
350
  // exists), but a row that EXISTS must be exact — a provider the policy
@@ -364,6 +424,8 @@ export function routeOfTask(store, taskId, ref, now) {
364
424
  candidates: candidates.candidates,
365
425
  overrides,
366
426
  pins: { plan: planPin.pin, build: buildPin.pin },
427
+ size: ref?.sizing ?? null,
428
+ headroom: store.planHeadroom(now),
367
429
  });
368
430
  return { kind: "route", route, source: "live" };
369
431
  }
@@ -424,9 +486,15 @@ export function agentChoicesFor(store, repo, route) {
424
486
  const buildProvider = route === null ? candidates.candidates.build.routine.provider : legOf(route, "build").provider;
425
487
  const configuredFor = (phase) => {
426
488
  const tiers = candidates.candidates[phase];
489
+ const everyOf = (one) => [
490
+ one.light ?? null,
491
+ one.routine,
492
+ one.strong,
493
+ ...Object.values(one.alternates ?? {}).flatMap(list => [...(list ?? [])]),
494
+ ];
427
495
  const own = phase === "repair"
428
- ? [tiers.routine, tiers.strong, candidates.candidates.build.routine, candidates.candidates.build.strong]
429
- : [tiers.routine, tiers.strong];
496
+ ? [tiers.routine, tiers.strong, ...everyOf(candidates.candidates.build)]
497
+ : phase === "review" ? [tiers.routine, tiers.strong] : everyOf(candidates.candidates[phase]);
430
498
  const out = [];
431
499
  for (const one of own) {
432
500
  if (one === null || out.some(seen => sameSpec(seen, one)))