toolroll 0.9.15 → 0.9.16

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 (67) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/assignment-brief.d.ts +10 -0
  3. package/dist/assignment-brief.js +49 -26
  4. package/dist/assignment.d.ts +8 -7
  5. package/dist/assignment.js +13 -8
  6. package/dist/browser/workspace.css +1 -1
  7. package/dist/browser/workspace.js +22 -22
  8. package/dist/browser-crew.js +1 -0
  9. package/dist/browser-workspace.d.ts +5 -0
  10. package/dist/chat-ask.d.ts +28 -0
  11. package/dist/chat-ask.js +77 -0
  12. package/dist/chat-channel.js +6 -1
  13. package/dist/chat-controls.d.ts +8 -0
  14. package/dist/chat-controls.js +3 -0
  15. package/dist/chat-delivery-state.d.ts +6 -1
  16. package/dist/chat-delivery-state.js +15 -2
  17. package/dist/chat-delivery.js +20 -3
  18. package/dist/chat-quiet.js +4 -2
  19. package/dist/chat-voice.d.ts +3 -6
  20. package/dist/chat-voice.js +8 -2
  21. package/dist/discord-chat.js +10 -0
  22. package/dist/lead-commitments.d.ts +84 -0
  23. package/dist/lead-commitments.js +208 -0
  24. package/dist/lead-context.d.ts +32 -3
  25. package/dist/lead-context.js +133 -19
  26. package/dist/lead-follow.js +3 -0
  27. package/dist/lead-identity.d.ts +23 -0
  28. package/dist/lead-identity.js +33 -0
  29. package/dist/lead-voice.d.ts +12 -9
  30. package/dist/lead-voice.js +25 -16
  31. package/dist/ledger-view.js +3 -2
  32. package/dist/mate-cli.d.ts +1 -1
  33. package/dist/mate-cli.js +8 -2
  34. package/dist/mate-contract.d.ts +1 -1
  35. package/dist/mate-contract.js +11 -4
  36. package/dist/mate-progress.js +5 -0
  37. package/dist/mate-tools.d.ts +18 -1
  38. package/dist/mate-tools.js +250 -6
  39. package/dist/mate.d.ts +9 -0
  40. package/dist/mate.js +34 -38
  41. package/dist/operate.js +2 -1
  42. package/dist/project-delete.js +3 -1
  43. package/dist/result-acts.d.ts +14 -0
  44. package/dist/result-acts.js +19 -0
  45. package/dist/retention.js +6 -0
  46. package/dist/serve.js +111 -29
  47. package/dist/slack-chat.js +10 -0
  48. package/dist/store.d.ts +51 -0
  49. package/dist/store.js +115 -1
  50. package/dist/surface.js +2 -2
  51. package/dist/task-status.d.ts +5 -15
  52. package/dist/task-status.js +18 -5
  53. package/dist/team-runtime.js +2 -1
  54. package/dist/teams-chat.js +4 -1
  55. package/dist/telegram-mate.d.ts +4 -1
  56. package/dist/telegram-mate.js +17 -1
  57. package/dist/telegram-progress.js +3 -2
  58. package/dist/telegram.js +43 -3
  59. package/dist/test-temp.js +1 -1
  60. package/dist/when-html.d.ts +8 -3
  61. package/dist/when-html.js +23 -3
  62. package/dist/work-index.d.ts +2 -0
  63. package/dist/work-index.js +9 -6
  64. package/dist/work-summary.d.ts +1 -1
  65. package/dist/workspace-ui.d.ts +2 -14
  66. package/dist/workspace-ui.js +4 -22
  67. package/package.json +1 -1
@@ -1,13 +1,14 @@
1
1
  /** Paginated, permission-bound summaries of saved task facts. These reads do
2
2
  * not verify artifacts or authorize task operations; a build Toolroll can't
3
3
  * yet confirm stopped is read through the store's own read-only probe. */
4
+ import { leadNameOf } from './lead-identity.js';
4
5
  import { createHash } from 'node:crypto';
5
6
  import { existsSync } from 'node:fs';
6
7
  import { join } from 'node:path';
7
8
  import { readProjectAccess } from './project-access.js';
8
9
  import { pauseOnTaskProviders, providerName, signInReason } from './provider-auth.js';
9
10
  import { scopeTermsProblem, stopFactOf } from './store.js';
10
- import { plainReasonOf, replacedWords, stageOfCode, taskStatusOf, workToneOf } from './task-status.js';
11
+ import { leadOnIt, plainReasonOf, replacedWords, stageOfCode, taskStatusOf, workToneOf } from './task-status.js';
11
12
  import { isCheckLevel } from './check-levels.js';
12
13
  import { MARKER } from './worktree.js';
13
14
  import { withFollowUps } from './result-follow-ups.js';
@@ -395,7 +396,9 @@ export function workIndexPage(store, now, access, options = {}) {
395
396
  const ref = store.lookupRef(taskId);
396
397
  return ref === null ? null : custody.readings.get(ref.id) ?? null;
397
398
  };
398
- return { items: page.map(row => itemOf(row, access.principal, probe)), projects, totals: rows[0] === undefined ? { ...EMPTY } : counts(rows[0]),
399
+ // A claimed row says the viewer's own name for their lead.
400
+ const leadName = page.some(row => n(row, 'lead_claim') !== 0) && access.principal === 'operator' && access.viewer != null ? leadNameOf(store, access.viewer) : undefined;
401
+ return { items: page.map(row => itemOf(row, access.principal, probe, leadName)), projects, totals: rows[0] === undefined ? { ...EMPTY } : counts(rows[0]),
399
402
  groups: rows[0] === undefined ? { ...NO_GROUPS } : groupCounts(rows[0]), nextCursor, limit, view };
400
403
  }
401
404
  export function workCountsByProject(store, now, access) {
@@ -441,13 +444,13 @@ function listChecksOf(own, level, follows) {
441
444
  return null;
442
445
  return { status: read.status, exitCode: null, head: null, level: read.level, running: read.running };
443
446
  }
444
- function itemOf(row, principal, probe) {
447
+ function itemOf(row, principal, probe, leadName) {
445
448
  const code = String(row['code']), id = String(row['id']);
446
449
  const custody = code === 'process-needs-attention' || (code === 'result-needs-attention' && n(row, 'custody_unresolved') === 1);
447
450
  const process = custody && probe !== undefined ? processNeedOf(probe(id)) : null;
448
451
  const waiting = process !== null && 'wait' in process;
449
452
  const need = n(row, 'needs') === 1 && !waiting, running = n(row, 'family_running') === 1;
450
- // The lead's claim (lead-voice.ts): the row still reads its own stage, worded "Your lead is on it".
453
+ // The lead's claim (lead-voice.ts): the row still reads its own stage, worded "<name> is on it".
451
454
  const lead = n(row, 'lead_claim') === 1 ? 'on-it' : n(row, 'lead_claim') === 2 ? 'lapsed' : null;
452
455
  const personNeed = (n(row, 'person_need') === 1 || need) && !waiting;
453
456
  const assignmentState = code === 'complete' ? 'complete' : code === 'ready-to-check' ? 'ready-to-check'
@@ -559,7 +562,7 @@ function itemOf(row, principal, probe) {
559
562
  reason: finished ? null : plainReasonOf(reading.stage, code, detail || null), report: s(row, 'result_role') === 'scout',
560
563
  checks: !finished ? null : listChecksOf(own, s(row, 'check_level'), row['check_follows']),
561
564
  completedBy: code !== 'complete' ? null : n(row, 'completed_by_lead') === 1 ? 'the lead' : String(row['checked_actor']).replace(/^(?:operator|coordinator|lead):/, ''),
562
- ...(lead === null ? {} : { lead }) });
565
+ ...(lead === null ? {} : { lead, ...(leadName === undefined ? {} : { leadName }) }) });
563
566
  label = shared.headline;
564
567
  detail = shared.sentence;
565
568
  // Every Needs you action wears its need's own words.
@@ -584,5 +587,5 @@ function itemOf(row, principal, probe) {
584
587
  ask: need ? ASKS[n(row, 'ask_rank')] ?? 'unblock' : null,
585
588
  chip: need ? askChipOf({ headline: shared.headline, need: shared.need?.key ?? null, planChanged, mismatch: verdict === 'refuted' && n(row, 'proof_failed_check') !== 1, ask: ASKS[n(row, 'ask_rank')] ?? 'unblock' }) : null,
586
589
  primaryAction, completion: code === 'complete' ? { actor: String(row['checked_actor']), at: String(row['checked_at']), digest: String(row['checked_digest']) } : null,
587
- evidence: 'recorded' };
590
+ evidence: 'recorded', ...(lead === 'on-it' ? { lead: leadOnIt(leadName) } : {}) };
588
591
  }
@@ -42,7 +42,7 @@ export declare function workDecisionAction(taskId: string, decision: NonNullable
42
42
  /** Pure counterpart for callers already holding the same facts. */
43
43
  export declare function workSummaryOf(facts: WorkFacts, principal: WorkPrincipal, resultDisplay?: DisplayStatus): WorkSummary;
44
44
  export type WorkSummaryAccess =
45
- /** `viewer`: the person reading, whose own lead's claim (lead-voice.ts) a task reads as "Your lead is on it". */
45
+ /** `viewer`: the person reading, whose own lead's claim (lead-voice.ts) a task reads as "<name> is on it". */
46
46
  {
47
47
  principal: "operator";
48
48
  repos: readonly string[] | null;
@@ -17,8 +17,8 @@
17
17
  * claimed without one.
18
18
  */
19
19
  import type { DispatchDiagnosis } from "./dispatch.js";
20
- import { ACCEPT_NEEDS_REASON, type AcceptLabel } from "./result-acts.js";
21
- export { ACCEPT_NEEDS_REASON };
20
+ import { ACCEPT_NEEDS_REASON, cantAcceptYetOf, evidenceProblemOf, type AcceptLabel, type EvidenceProblem } from "./result-acts.js";
21
+ export { ACCEPT_NEEDS_REASON, cantAcceptYetOf, evidenceProblemOf, type EvidenceProblem };
22
22
  import { type ProofVerdict } from "./proof.js";
23
23
  import type { ReviewRetryState, TaskState } from "./store.js";
24
24
  import type { TaskControlView } from "./task-control.js";
@@ -113,14 +113,6 @@ export type DisplayStatus = {
113
113
  tone: StatusTone;
114
114
  action: NextAction | null;
115
115
  };
116
- /** What kind of evidence problem a verdict records: a check the machine
117
- * actually ran and saw fail, evidence that contradicts the sealed record
118
- * (a mismatched changed-path claim, an altered criterion, a caveat that
119
- * contradicts a met verdict), or evidence that is simply absent. A generic
120
- * refuted verdict is NOT a failed test claim — only the verify-command
121
- * reasons say a check failed. */
122
- export type EvidenceProblem = "checks-failed" | "mismatched" | "missing" | "none";
123
- export declare function evidenceProblemOf(verdict: ProofVerdict | null, reasons: readonly string[]): EvidenceProblem;
124
116
  /** The exit code of the failed check, when the recorded reason names one. */
125
117
  export declare function failedCheckExit(reasons: readonly string[]): number | null;
126
118
  /** The observed publication record in words. Each state is a distinct
@@ -192,10 +184,6 @@ export declare function dispatchActionLabel(dispatch: DispatchDiagnosis | null):
192
184
  /** A finished result's display status in the shared headline's words: a
193
185
  * failed project check is Failed, a result missing its record needs a
194
186
  * person, every other saved result is Ready for review. Token and action stay. */
195
- /** Why a result can't be accepted as it stands, in one plain line, or null
196
- * when nothing refutes it (a person's acceptance settles it). The result page
197
- * shows it before Request changes, its one ink act. */
198
- export declare function cantAcceptYetOf(verdict: ProofVerdict | null, reasons: readonly string[], accepted: boolean): string | null;
199
187
  /** The result's headline while its report disagrees with its saved changes. */
200
188
  export declare const MISMATCH_HEADLINE = "The report doesn't match the changes";
201
189
  /** One way the report disagrees with the saved changes, in plain words: the
@@ -16,10 +16,11 @@
16
16
  * observed" name observed publication records, and deployment is never
17
17
  * claimed without one.
18
18
  */
19
- import { ACCEPT_NEEDS_REASON } from "./result-acts.js";
20
- export { ACCEPT_NEEDS_REASON };
19
+ import { ACCEPT_NEEDS_REASON, FAILED_CHECK, cantAcceptYetOf, evidenceProblemOf } from "./result-acts.js";
20
+ export { ACCEPT_NEEDS_REASON, cantAcceptYetOf, evidenceProblemOf };
21
21
  import { GOAL_ASSESSMENT_PENDING, manualReviewOnly, plainReasonWords } from "./proof.js";
22
22
  import { OPEN_RESULT, plainReasonOf, stageOfCode, taskStatusOf, workToneOf } from "./task-status.js";
23
+ import { whenUtc } from "./when-html.js";
23
24
  export const WORK_VIEWS = [
24
25
  { key: "all", label: "All", hint: "Every task in view, most urgent first.", empty: "Nothing is in progress. Describe work in chat or add a task, and it appears here." },
25
26
  { key: "needs-you", label: "Needs you", hint: "Tasks waiting on an answer, approval, or your inspection.", empty: "Nothing needs you right now. Queued and running work continues on its own." },
@@ -69,14 +70,6 @@ export function reviewStatusOf(review) {
69
70
  return null;
70
71
  return { token: "reviewing", label: "Reviewing", detail: "An automatic review is reading this result before it reaches you.", tone: "live", action: { label: OPEN_RESULT, kind: "open-result" } };
71
72
  }
72
- const FAILED_CHECK = /^the repository's approved verification command exited (-?[0-9]+)/;
73
- export function evidenceProblemOf(verdict, reasons) {
74
- if (verdict === "verified" || verdict === "attested")
75
- return "none";
76
- if (verdict === "refuted")
77
- return reasons.some(reason => FAILED_CHECK.test(reason)) ? "checks-failed" : "mismatched";
78
- return "missing";
79
- }
80
73
  /** The exit code of the failed check, when the recorded reason names one. */
81
74
  export function failedCheckExit(reasons) {
82
75
  for (const reason of reasons) {
@@ -314,17 +307,6 @@ const VERIFIED_RESULT = new Set(["ready-to-review", "pr-opened", "merge-observed
314
307
  /** A finished result's display status in the shared headline's words: a
315
308
  * failed project check is Failed, a result missing its record needs a
316
309
  * person, every other saved result is Ready for review. Token and action stay. */
317
- /** Why a result can't be accepted as it stands, in one plain line, or null
318
- * when nothing refutes it (a person's acceptance settles it). The result page
319
- * shows it before Request changes, its one ink act. */
320
- export function cantAcceptYetOf(verdict, reasons, accepted) {
321
- if (accepted || verdict !== "refuted")
322
- return null;
323
- // A mismatch is the status card's own headline; here only what accepting takes.
324
- return evidenceProblemOf(verdict, reasons) === "checks-failed"
325
- ? "Can't accept yet: the project's check failed on these changes."
326
- : ACCEPT_NEEDS_REASON;
327
- }
328
310
  /** The result's headline while its report disagrees with its saved changes. */
329
311
  export const MISMATCH_HEADLINE = "The report doesn't match the changes";
330
312
  const OVERCLAIMED = /^claimed changed paths? not in the sealed diff: ([\s\S]+)$/;
@@ -548,7 +530,7 @@ export function learningHtml(view, csrf, canManage, source) {
548
530
  const cards = lessons.map(l => `<article class="card" data-lesson="${l.id}"><p><strong>${e(l.payload.observation)}</strong></p><p>${e(l.payload.action)}</p><p class="meta">${l.payload.kind === 'system' ? 'System suggestion · No change applied' : e(l.status === 'adopted' ? 'Adopted advice' : l.status === 'disabled' ? 'Disabled' : 'Proposed lesson')}</p><details><summary>Source and use</summary><p><a href="/r/${l.source}">Result #${l.source}</a> · <a href="/r/${l.reviewer}">Review #${l.reviewer}</a></p><p>${e(l.payload.paths.join(', '))} · ${e(l.payload.phases.join(', '))} · ${e({ darwin: "macOS", win32: "Windows", linux: "Linux" }[l.payload.platform] ?? l.payload.platform)}</p><ul>${evidence(l.payload.evidence, l.source)}</ul>${l.payload.kind !== 'project' ? '' : `<p class="meta">${l.status === 'proposed' ? 'Save permits advisory reuse when enabled and applicable. It does not prove a remedy works.' : 'Disabling affects future runs. Active snapshots, code and approvals stay unchanged.'}</p>${l.status === 'proposed' ? act('adopt', 'Save lesson', l) : ''}`}</details>${l.payload.kind === 'project' && l.status === 'adopted' ? act('disable', 'Disable lesson', l) : ''}</article>`).join('');
549
531
  if (source !== undefined)
550
532
  return lessons.length ? `<details class="learning result-learning"><summary>Learned from this task</summary>${cards}<a href="/settings/learning?repo=${encodeURIComponent(view.repo)}">Learning settings</a></details>` : '';
551
- const history = view.events.map(ev => `<article class="card" data-learning-event="${e(ev.action)}"><p><strong>${e({ assessment: { propose: 'Learning suggested', none: 'No lesson needed', unassessed: 'Learning not assessed', invalid: 'Learning assessment invalid' }[ev.after] ?? 'Learning not assessed', proposal: 'Suggestion recorded', adopt: 'Lesson adopted', disable: 'Lesson disabled', reset: 'Learning reset', enable: 'Reuse enabled', pause: 'Reuse paused', reuse: 'Run context saved', failure: 'Learning issue', capture: 'Capture finished' }[ev.action] ?? ev.action)}</strong>${ev.action === "reuse" ? ` · ${e(ev.after)}` : ""}</p><p class="meta"><time datetime="${e(ev.at)}">${e(ev.at.replace("T", " ").replace(/\.\d+Z$/, " UTC"))}</time> · ${e(ev.actor)}${ev.run === null ? '' : ` · <a href="/r/${ev.run}">Run #${ev.run}</a>${ev.outcome === null ? "" : ` · ${e(ev.outcome)}`}`}</p><details><summary>Details</summary><p>${e(ev.before)} → ${e(ev.after)}</p><p>${e(ev.reason)}</p>${ev.lesson === null ? '' : `<p>Lesson #${ev.lesson}</p>`}<ul>${evidence(ev.evidence, ev.run)}</ul>${ev.snapshot === null ? "" : `<details><summary>Exact context</summary><pre>${e(ev.snapshot)}</pre></details>`}</details></article>`).join('');
533
+ const history = view.events.map(ev => `<article class="card" data-learning-event="${e(ev.action)}"><p><strong>${e({ assessment: { propose: 'Learning suggested', none: 'No lesson needed', unassessed: 'Learning not assessed', invalid: 'Learning assessment invalid' }[ev.after] ?? 'Learning not assessed', proposal: 'Suggestion recorded', adopt: 'Lesson adopted', disable: 'Lesson disabled', reset: 'Learning reset', enable: 'Reuse enabled', pause: 'Reuse paused', reuse: 'Run context saved', failure: 'Learning issue', capture: 'Capture finished' }[ev.action] ?? ev.action)}</strong>${ev.action === "reuse" ? ` · ${e(ev.after)}` : ""}</p><p class="meta">${whenUtc(ev.at)} · ${e(ev.actor)}${ev.run === null ? '' : ` · <a href="/r/${ev.run}">Run #${ev.run}</a>${ev.outcome === null ? "" : ` · ${e(ev.outcome)}`}`}</p><details><summary>Details</summary><p>${e(ev.before)} → ${e(ev.after)}</p><p>${e(ev.reason)}</p>${ev.lesson === null ? '' : `<p>Lesson #${ev.lesson}</p>`}<ul>${evidence(ev.evidence, ev.run)}</ul>${ev.snapshot === null ? "" : `<details><summary>Exact context</summary><pre>${e(ev.snapshot)}</pre></details>`}</details></article>`).join('');
552
534
  return `<section class="learning">${view.damaged ? '<p class="problem" role="alert">Some lessons no longer verify and are excluded from reuse.</p>' : ''}<p>${view.enabled ? 'Use adopted lessons: on' : 'Use adopted lessons: off'}</p>${act(view.enabled ? 'pause' : 'enable', view.enabled ? 'Pause reuse' : 'Enable reuse')}${cards || '<p>No lessons yet. Reviews may suggest useful advice here.</p>'}<details><summary>Reset learning</summary><p>Disable all adopted lessons and future reuse. Keep history, code, approvals and active run snapshots.</p>${act('reset', 'Reset learning')}</details><h2>Changes</h2>${history || '<p>No learning changes yet.</p>'}${view.next === null ? '' : `<a class="button-link" href="/settings/learning?repo=${encodeURIComponent(view.repo)}&before=${view.next}">Older changes</a>`}</section>`;
553
535
  }
554
536
  /** The first signed requirement a finished attempt missed (failed or unanswered; a person's own check is not a miss),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolroll",
3
- "version": "0.9.15",
3
+ "version": "0.9.16",
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"