toolroll 0.9.54 → 0.9.56
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.
- package/CHANGELOG.md +19 -0
- package/README.md +29 -12
- package/dist/action-ledger.d.ts +2 -4
- package/dist/action-ledger.js +2 -10
- package/dist/agent-onboard.js +4 -1
- package/dist/approval-policy.d.ts +5 -5
- package/dist/approval-policy.js +4 -4
- package/dist/approval-rules-ui.d.ts +3 -2
- package/dist/approval-rules-ui.js +17 -22
- package/dist/assignment-ui.d.ts +3 -2
- package/dist/assignment-ui.js +4 -11
- package/dist/backup-ui.d.ts +2 -1
- package/dist/backup-ui.js +16 -27
- package/dist/brand-mark.d.ts +3 -2
- package/dist/brand-mark.js +4 -4
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +25 -25
- package/dist/browser-shell.d.ts +2 -1
- package/dist/browser-shell.js +9 -9
- package/dist/browser-workspace.d.ts +14 -15
- package/dist/browser-workspace.js +0 -3
- package/dist/budget-alerts.js +3 -3
- package/dist/chat-actions.d.ts +23 -18
- package/dist/chat-actions.js +110 -88
- package/dist/chat-ask.d.ts +2 -2
- package/dist/chat-ask.js +5 -5
- package/dist/chat-channel.d.ts +15 -15
- package/dist/chat-channel.js +20 -20
- package/dist/chat-controls.d.ts +0 -4
- package/dist/chat-controls.js +0 -1
- package/dist/chat-core.js +1 -1
- package/dist/chat-delivery-state.js +2 -2
- package/dist/chat-delivery.d.ts +2 -2
- package/dist/chat-delivery.js +27 -34
- package/dist/chat-flow.js +3 -3
- package/dist/chat-messages.d.ts +1 -1
- package/dist/chat-messages.js +4 -4
- package/dist/chat-migration.d.ts +1 -1
- package/dist/chat-migration.js +11 -11
- package/dist/chat-polish.d.ts +5 -4
- package/dist/chat-polish.js +9 -10
- package/dist/chat-rooms.js +3 -3
- package/dist/chat-warmth.d.ts +2 -2
- package/dist/check-levels-ui.d.ts +2 -1
- package/dist/check-levels-ui.js +26 -53
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +15 -10
- package/dist/coding-shipping-ui.d.ts +2 -1
- package/dist/coding-shipping-ui.js +6 -6
- package/dist/coding-ui.d.ts +3 -1
- package/dist/coding-ui.js +30 -29
- package/dist/contracts/chat-actions.d.ts +32 -27
- package/dist/contracts/chat-actions.js +29 -11
- package/dist/contracts/chat-content.d.ts +1 -1
- package/dist/contracts/chat-content.js +1 -1
- package/dist/contracts/chat-tables.js +4 -4
- package/dist/contracts/cli.d.ts +1 -3
- package/dist/contracts/console-api.d.ts +7 -7
- package/dist/contracts/console-api.js +13 -12
- package/dist/contracts/flow-propose.d.ts +3 -3
- package/dist/contracts/flow.d.ts +132 -125
- package/dist/contracts/flow.js +27 -12
- package/dist/contracts/health.d.ts +1 -3
- package/dist/contracts/lead-context.d.ts +1 -1
- package/dist/contracts/lead-context.js +2 -2
- package/dist/contracts/lead-tools.d.ts +20 -18
- package/dist/contracts/lead-tools.js +15 -15
- package/dist/contracts/stage-output.d.ts +1 -1
- package/dist/contracts/stage-output.js +1 -1
- package/dist/contracts/store-json.d.ts +23 -22
- package/dist/contracts/store-json.js +17 -17
- package/dist/contracts/{teammate-turn.d.ts → subagent-turn.d.ts} +5 -5
- package/dist/contracts/{teammate-turn.js → subagent-turn.js} +7 -7
- package/dist/control-ui.d.ts +5 -4
- package/dist/control-ui.js +9 -34
- package/dist/converse.d.ts +23 -23
- package/dist/converse.js +28 -28
- package/dist/coordinator-proposals.d.ts +2 -2
- package/dist/coordinator-proposals.js +1 -1
- package/dist/coordinator.js +1 -1
- package/dist/credentials-ui.d.ts +5 -4
- package/dist/credentials-ui.js +23 -24
- package/dist/demo-chat.d.ts +3 -3
- package/dist/demo-chat.js +29 -76
- package/dist/deprecations.d.ts +12 -0
- package/dist/deprecations.js +20 -0
- package/dist/desktop-update-gate.js +8 -4
- package/dist/desktop-update.js +6 -1
- package/dist/discord-chat.js +6 -6
- package/dist/discord-settings.d.ts +3 -2
- package/dist/discord-settings.js +17 -42
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +1 -1
- package/dist/envelope.js +10 -1
- package/dist/evidence-pack.d.ts +2 -1
- package/dist/evidence-pack.js +45 -49
- package/dist/export-ui.d.ts +7 -2
- package/dist/export-ui.js +4 -10
- package/dist/export.js +7 -7
- package/dist/flow-engine.d.ts +2 -2
- package/dist/flow-engine.js +21 -21
- package/dist/flow-gallery-ui.d.ts +4 -3
- package/dist/flow-gallery-ui.js +28 -43
- package/dist/flow-insights.d.ts +1 -1
- package/dist/flow-share.js +13 -8
- package/dist/flow-starters-ui.d.ts +2 -1
- package/dist/flow-starters-ui.js +12 -15
- package/dist/flow-steps.d.ts +5 -5
- package/dist/flow-steps.js +19 -19
- package/dist/flow-triggers.d.ts +2 -1
- package/dist/flow-triggers.js +17 -15
- package/dist/flows-cli.js +1 -1
- package/dist/flows-ui.d.ts +4 -3
- package/dist/flows-ui.js +38 -49
- package/dist/flows.js +21 -20
- package/dist/guides.js +112 -6
- package/dist/html.d.ts +76 -0
- package/dist/html.js +173 -0
- package/dist/integrations-ui.d.ts +2 -1
- package/dist/integrations-ui.js +19 -23
- package/dist/invoke.js +1 -1
- package/dist/kits-ui.d.ts +5 -4
- package/dist/kits-ui.js +22 -24
- package/dist/kits.d.ts +12 -12
- package/dist/kits.js +31 -31
- package/dist/knowledge-ui.d.ts +6 -5
- package/dist/knowledge-ui.js +26 -25
- package/dist/{mate-cli.d.ts → lead-cli.d.ts} +11 -11
- package/dist/{mate-cli.js → lead-cli.js} +38 -38
- package/dist/lead-commitments.js +4 -4
- package/dist/lead-context.d.ts +12 -2
- package/dist/lead-context.js +15 -17
- package/dist/{mate-contract.d.ts → lead-contract.d.ts} +3 -3
- package/dist/{mate-contract.js → lead-contract.js} +7 -6
- package/dist/{mate-doors.d.ts → lead-doors.d.ts} +6 -6
- package/dist/{mate-doors.js → lead-doors.js} +24 -24
- package/dist/lead-follow.d.ts +5 -5
- package/dist/lead-follow.js +9 -9
- package/dist/lead-people.d.ts +5 -5
- package/dist/lead-people.js +14 -14
- package/dist/{mate-progress.d.ts → lead-progress.d.ts} +9 -9
- package/dist/{mate-progress.js → lead-progress.js} +3 -3
- package/dist/{mate-tools.d.ts → lead-tools.d.ts} +19 -19
- package/dist/{mate-tools.js → lead-tools.js} +109 -101
- package/dist/{mate.d.ts → lead.d.ts} +27 -27
- package/dist/{mate.js → lead.js} +102 -102
- package/dist/ledger-view.d.ts +2 -1
- package/dist/ledger-view.js +27 -29
- package/dist/limits-ui.d.ts +3 -2
- package/dist/limits-ui.js +8 -8
- package/dist/live-bus.d.ts +50 -8
- package/dist/live-bus.js +133 -50
- package/dist/mcp-connect.d.ts +1 -1
- package/dist/mcp-connect.js +3 -3
- package/dist/mcp-core.js +1 -1
- package/dist/mcp-oauth.js +35 -36
- package/dist/memory-pass.d.ts +2 -2
- package/dist/memory-pass.js +7 -7
- package/dist/models-ui.d.ts +2 -1
- package/dist/models-ui.js +25 -35
- package/dist/monitoring-ui.d.ts +4 -3
- package/dist/monitoring-ui.js +13 -26
- package/dist/notification-rows.d.ts +2 -2
- package/dist/openrouter-models.d.ts +2 -1
- package/dist/openrouter-models.js +5 -9
- package/dist/operate.d.ts +3 -3
- package/dist/operate.js +46 -41
- package/dist/people-audit-ui.d.ts +2 -1
- package/dist/people-audit-ui.js +13 -13
- package/dist/policy-ui.d.ts +3 -2
- package/dist/policy-ui.js +17 -23
- package/dist/policy.d.ts +1 -1
- package/dist/principal.d.ts +1 -1
- package/dist/principal.js +2 -2
- package/dist/project-delete-ui.d.ts +4 -3
- package/dist/project-delete-ui.js +16 -28
- package/dist/project-delete.d.ts +1 -1
- package/dist/project-delete.js +26 -26
- package/dist/project-memory.js +1 -1
- package/dist/project-tools.d.ts +1 -1
- package/dist/project-tools.js +1 -1
- package/dist/pull-request-ui.d.ts +2 -1
- package/dist/pull-request-ui.js +17 -32
- package/dist/recipe-ui.d.ts +8 -7
- package/dist/recipe-ui.js +39 -32
- package/dist/release-adapters.d.ts +60 -0
- package/dist/release-adapters.js +435 -0
- package/dist/release-cli.d.ts +24 -0
- package/dist/release-cli.js +181 -0
- package/dist/release-gate.d.ts +80 -0
- package/dist/release-gate.js +150 -0
- package/dist/release-links.d.ts +29 -0
- package/dist/release-links.js +102 -0
- package/dist/release.d.ts +331 -0
- package/dist/release.js +550 -0
- package/dist/reply-shape.d.ts +2 -1
- package/dist/reply-shape.js +5 -5
- package/dist/repository-context-ui.d.ts +2 -1
- package/dist/repository-context-ui.js +2 -7
- package/dist/request-budget-ui.d.ts +2 -1
- package/dist/request-budget-ui.js +16 -16
- package/dist/result-review.d.ts +2 -3
- package/dist/result-review.js +3 -9
- package/dist/retention-ui.d.ts +7 -1
- package/dist/retention-ui.js +7 -11
- package/dist/retention.js +4 -4
- package/dist/scope.d.ts +2 -2
- package/dist/scout-report.d.ts +1 -1
- package/dist/scout-report.js +1 -1
- package/dist/serve.d.ts +11 -2
- package/dist/serve.js +105 -99
- package/dist/server/chat.d.ts +7 -10
- package/dist/server/chat.js +984 -1021
- package/dist/server/chrome.d.ts +291 -0
- package/dist/server/chrome.js +747 -0
- package/dist/server/console.css +2819 -0
- package/dist/server/flows.d.ts +2 -6
- package/dist/server/flows.js +854 -773
- package/dist/server/guards.d.ts +1 -1
- package/dist/server/guards.js +8 -5
- package/dist/server/handler-context.d.ts +1 -1
- package/dist/server/handler-context.js +1 -1
- package/dist/server/handler-registry.d.ts +8 -6
- package/dist/server/handler-registry.js +14 -2
- package/dist/server/http.d.ts +274 -0
- package/dist/server/http.js +239 -0
- package/dist/server/live.d.ts +34 -0
- package/dist/server/live.js +309 -0
- package/dist/server/pages.d.ts +2 -6
- package/dist/server/pages.js +808 -789
- package/dist/server/people-tokens.d.ts +1 -6
- package/dist/server/people-tokens.js +581 -573
- package/dist/server/remote.d.ts +4 -7
- package/dist/server/remote.js +39 -70
- package/dist/server/render-chat.d.ts +402 -0
- package/dist/server/render-chat.js +927 -0
- package/dist/server/render-pages.d.ts +535 -0
- package/dist/server/render-pages.js +1277 -0
- package/dist/server/render-people.d.ts +32 -0
- package/dist/server/render-people.js +121 -0
- package/dist/server/render-results.d.ts +744 -0
- package/dist/server/render-results.js +2237 -0
- package/dist/server/render-settings.d.ts +154 -0
- package/dist/server/render-settings.js +580 -0
- package/dist/server/render-tasks.d.ts +632 -0
- package/dist/server/render-tasks.js +2345 -0
- package/dist/server/request-context.d.ts +46 -0
- package/dist/server/request-context.js +17 -0
- package/dist/server/route-table.d.ts +25 -3
- package/dist/server/route-table.js +167 -77
- package/dist/server/runtime.d.ts +28 -20
- package/dist/server/runtime.js +7 -1
- package/dist/server/session.d.ts +138 -0
- package/dist/server/session.js +94 -0
- package/dist/server/settings.d.ts +1 -5
- package/dist/server/settings.js +2186 -2121
- package/dist/server/task-views.d.ts +11 -4
- package/dist/server/task-views.js +26 -22
- package/dist/server/tasks.d.ts +9 -9
- package/dist/server/tasks.js +1489 -1420
- package/dist/server-telemetry.d.ts +4 -3
- package/dist/server-telemetry.js +4 -8
- package/dist/setup-guide.d.ts +2 -2
- package/dist/skills-ui.d.ts +4 -3
- package/dist/skills-ui.js +14 -17
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +2 -1
- package/dist/slack-chat.d.ts +2 -2
- package/dist/slack-chat.js +6 -6
- package/dist/slack-settings.d.ts +3 -2
- package/dist/slack-settings.js +17 -42
- package/dist/spend-ui.d.ts +5 -4
- package/dist/spend-ui.js +17 -40
- package/dist/spend.d.ts +14 -14
- package/dist/spend.js +31 -31
- package/dist/sso-ui.d.ts +3 -2
- package/dist/sso-ui.js +26 -27
- package/dist/storage-ui.d.ts +2 -1
- package/dist/storage-ui.js +14 -33
- package/dist/store-baseline.d.ts +19 -0
- package/dist/store-baseline.js +2873 -0
- package/dist/store.d.ts +197 -249
- package/dist/store.js +747 -3084
- package/dist/subagent-admin.d.ts +53 -0
- package/dist/{teammate-admin.js → subagent-admin.js} +37 -37
- package/dist/{teammate-desk.d.ts → subagent-desk.d.ts} +22 -23
- package/dist/{teammate-desk.js → subagent-desk.js} +26 -35
- package/dist/{teammate-memory.d.ts → subagent-memory.d.ts} +17 -17
- package/dist/{teammate-memory.js → subagent-memory.js} +41 -41
- package/dist/{teammate-question.d.ts → subagent-question.d.ts} +6 -6
- package/dist/{teammate-question.js → subagent-question.js} +17 -17
- package/dist/{teammate-tools.d.ts → subagent-tools.d.ts} +18 -18
- package/dist/{teammate-tools.js → subagent-tools.js} +36 -36
- package/dist/{teammate-week.d.ts → subagent-week.d.ts} +10 -10
- package/dist/{teammate-week.js → subagent-week.js} +23 -23
- package/dist/{teammate-work.d.ts → subagent-work.d.ts} +21 -21
- package/dist/{teammate-work.js → subagent-work.js} +50 -50
- package/dist/subagents-ui.d.ts +22 -0
- package/dist/subagents-ui.js +137 -0
- package/dist/{teammates.d.ts → subagents.d.ts} +11 -11
- package/dist/{teammates.js → subagents.js} +21 -21
- package/dist/subscription-chat.d.ts +13 -13
- package/dist/subscription-chat.js +19 -19
- package/dist/surface.d.ts +19 -2
- package/dist/surface.js +20 -33
- package/dist/task-composer.d.ts +2 -1
- package/dist/task-composer.js +7 -7
- package/dist/task-status.d.ts +4 -3
- package/dist/task-status.js +15 -16
- package/dist/team-cli.d.ts +1 -0
- package/dist/team-cli.js +14 -2
- package/dist/team-http.d.ts +5 -9
- package/dist/team-http.js +5 -63
- package/dist/team-leads.js +13 -13
- package/dist/team-runtime.d.ts +2 -2
- package/dist/team-runtime.js +8 -8
- package/dist/team-ui.d.ts +2 -1
- package/dist/team-ui.js +2 -5
- package/dist/teams-chat.js +6 -6
- package/dist/teams-settings.d.ts +3 -2
- package/dist/teams-settings.js +20 -31
- package/dist/telegram-flow.d.ts +3 -3
- package/dist/telegram-flow.js +3 -3
- package/dist/{telegram-mate.d.ts → telegram-lead.d.ts} +5 -5
- package/dist/{telegram-mate.js → telegram-lead.js} +27 -27
- package/dist/telegram-settings.d.ts +3 -2
- package/dist/telegram-settings.js +14 -25
- package/dist/telegram-status.js +1 -1
- package/dist/telegram-team.d.ts +1 -1
- package/dist/telegram-team.js +5 -5
- package/dist/telegram.d.ts +1 -1
- package/dist/telegram.js +9 -30
- package/dist/text-limits.d.ts +18 -18
- package/dist/text-limits.js +18 -18
- package/dist/toolroll-update-ui.d.ts +3 -2
- package/dist/toolroll-update-ui.js +35 -39
- package/dist/toolroll-update.d.ts +8 -0
- package/dist/toolroll-update.js +45 -8
- package/dist/tools-ui.d.ts +5 -2
- package/dist/tools-ui.js +31 -31
- package/dist/when-html.d.ts +11 -3
- package/dist/when-html.js +5 -6
- package/dist/workspace-ui.d.ts +4 -2
- package/dist/workspace-ui.js +10 -10
- package/package.json +1 -2
- package/dist/server/shared.d.ts +0 -3151
- package/dist/server/shared.js +0 -12001
- package/dist/teammate-admin.d.ts +0 -53
- package/dist/teammates-ui.d.ts +0 -20
- package/dist/teammates-ui.js +0 -166
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.56 — 2026-10-09
|
|
4
|
+
|
|
5
|
+
- **One lead.** Your assistant in the console, on your phone, in chat and in
|
|
6
|
+
the CLI is the same lead. Teammates are now its subagents, with their
|
|
7
|
+
personalities and rules: "ask Rosa to…", or from a flow step.
|
|
8
|
+
- **Retiring soon.** The central team service and native coding sessions are
|
|
9
|
+
hidden and warn when used; they are removed next release.
|
|
10
|
+
- **Fresh baseline.** New installs start from a baseline schema. Databases
|
|
11
|
+
older than 0.5.0 are asked to update through 0.9.x first.
|
|
12
|
+
|
|
13
|
+
## 0.9.55 — 2026-10-09
|
|
14
|
+
|
|
15
|
+
- **One door per route.** Every page's access comes from one table with one
|
|
16
|
+
handler each; pages escape automatically and forms carry CSRF protection.
|
|
17
|
+
- **One live stream.** It re-checks your session and project access before
|
|
18
|
+
every update.
|
|
19
|
+
- **One-command release.** `toolroll release <branch>` runs a whole release
|
|
20
|
+
after your scope approval and never merges on pending or failed CI.
|
|
21
|
+
|
|
3
22
|
## 0.9.54 — 2026-10-09
|
|
4
23
|
|
|
5
24
|
- **One chat core.** Telegram, Slack, Discord and Teams now share one chat
|
package/README.md
CHANGED
|
@@ -366,7 +366,7 @@ for people splitting those parts across machines.
|
|
|
366
366
|
Specialized views remain in **Tools** and **Settings**: the activity ledger, the review cockpit,
|
|
367
367
|
the fleet,
|
|
368
368
|
people (invite a second approver), the operating mode (a signed, expiring
|
|
369
|
-
envelope that pre-approves your own filings), and **chat** —
|
|
369
|
+
envelope that pre-approves your own filings), and **chat** — your lead, one
|
|
370
370
|
conversation across every project, which only ever proposes.
|
|
371
371
|
|
|
372
372
|
### In the terminal
|
|
@@ -383,7 +383,7 @@ toolroll decide <id> --choose <option> # answer a parked decision
|
|
|
383
383
|
toolroll task show retries # attempts, outcome, where the branch is
|
|
384
384
|
|
|
385
385
|
toolroll task add "Why does the login test flake?" --id flaky --report # a scout
|
|
386
|
-
toolroll chat --say "what is waiting on me across every project?" #
|
|
386
|
+
toolroll chat --say "what is waiting on me across every project?" # your lead
|
|
387
387
|
```
|
|
388
388
|
|
|
389
389
|
Every command takes `--json` and answers with one envelope; every mutation
|
|
@@ -477,7 +477,7 @@ toolroll task block api --on schema # api waits for schema
|
|
|
477
477
|
toolroll ready --json # what could be dispatched now
|
|
478
478
|
toolroll claim schema --runner builder-1 --key dispatch-schema
|
|
479
479
|
toolroll heartbeat <lease> # still working
|
|
480
|
-
toolroll release <lease>
|
|
480
|
+
toolroll worker release <lease> # done holding it
|
|
481
481
|
```
|
|
482
482
|
|
|
483
483
|
Four properties make that loop safe to run unattended.
|
|
@@ -540,9 +540,10 @@ service unit never carries it. Cron remains first-class if you prefer it —
|
|
|
540
540
|
cron's cadence, and a stray cron tick alongside a watch is safe (ordinary
|
|
541
541
|
claims settle the race), it just is not needed.
|
|
542
542
|
|
|
543
|
-
An honesty note for Windows: every pull request
|
|
544
|
-
runs the native
|
|
545
|
-
|
|
543
|
+
An honesty note for Windows: every pull request builds Toolroll on Windows
|
|
544
|
+
with Node 22 and runs the native Job Object containment tests there; the full
|
|
545
|
+
suite runs on Ubuntu (Node 22 and 24), and on macOS nightly and for every
|
|
546
|
+
release. Approved setup and verification commands use
|
|
546
547
|
Windows' native command shell. The scheduled-task definition follows the Task
|
|
547
548
|
Scheduler XML schema and every `schtasks` interaction is covered by scripted
|
|
548
549
|
tests. A physical Windows install has not yet been certified; the exact
|
|
@@ -807,21 +808,37 @@ contents or command lines, with credential-shaped lines redacted at
|
|
|
807
808
|
write. Digits focus one pane, `a` shows them all, `q` leaves. Outside a
|
|
808
809
|
terminal, or with `--json`, it prints one snapshot and exits.
|
|
809
810
|
|
|
810
|
-
## The
|
|
811
|
+
## The lead, its subagents, and the gateway
|
|
811
812
|
|
|
812
|
-
`/chat` in the console (or `toolroll chat` in the terminal
|
|
813
|
-
conversation across every project you serve.
|
|
813
|
+
`/chat` in the console (or `toolroll chat` in the terminal, or your chat app on
|
|
814
|
+
the phone) is one conversation with your lead across every project you serve.
|
|
815
|
+
The console, the terminal, Telegram, Slack, Discord and Teams all talk to the
|
|
816
|
+
same lead with the same tools. The lead reads the fleet
|
|
814
817
|
and **only proposes**: file a task (or a scout), move one to the front,
|
|
815
818
|
reserve it for a worker, hold it, rewrite a scope, retry/replace/unlink a
|
|
816
819
|
terminal dependency, guide a task's next attempt, cancel, or suggest an answer to a parked decision. Every
|
|
817
820
|
proposal is a card you confirm, with
|
|
818
821
|
every consequence and the builder's recommendation shown beside the
|
|
819
|
-
|
|
822
|
+
lead's pick; a scope the lead wrote never seals under an operating mode.
|
|
820
823
|
Absolute paths, internal digests and account names are redacted; source context includes relative file citations. Direct API use spends
|
|
821
824
|
against a ceiling you set per conversation. The console keeps a live pulse for every
|
|
822
825
|
admitted project beside that shared thread, with one-click fleet questions
|
|
823
826
|
and a direct road to each project's board.
|
|
824
827
|
|
|
828
|
+
The lead has **subagents**: named helpers, each with its own personality,
|
|
829
|
+
instructions and rules (what it does on its own, what it asks first, what it
|
|
830
|
+
never does). Say "ask Maya to draft the refund reply" and the lead shows a card
|
|
831
|
+
with exactly what goes to Maya; once you confirm, Maya works it within her rules
|
|
832
|
+
and her answer comes back to you. A flow step can call a subagent by name too.
|
|
833
|
+
They live in **Settings → Lead → Subagents** ([guide](docs/guide/subagents.md)).
|
|
834
|
+
Subagents used to be called AI teammates; each carried over unchanged.
|
|
835
|
+
|
|
836
|
+
The central team service (`toolroll connect`, the central `toolroll lead
|
|
837
|
+
list|create|update|member|transfer`, `toolroll conversation …`, team chat) and
|
|
838
|
+
native coding sessions (`toolroll session …`, the `/code` page) are deprecated:
|
|
839
|
+
they still work for this release, hidden from help and the console, print one warning
|
|
840
|
+
when used, and are removed in the next minor release.
|
|
841
|
+
|
|
825
842
|
Every task has an **Overview / Ask** switch. **Ask** opens a focused companion
|
|
826
843
|
to that task without creating another conversation: Toolroll attaches
|
|
827
844
|
the current task to each new message, keeps the live status beside the thread,
|
|
@@ -830,7 +847,7 @@ result inspection. Proposed guidance is inert until you confirm its card, then i
|
|
|
830
847
|
reaches the next attempt without interrupting work already running.
|
|
831
848
|
|
|
832
849
|
New work starts the same way: describe the outcome once in ordinary language.
|
|
833
|
-
The
|
|
850
|
+
The lead infers a concise title, narrow scope, safe non-goals, and proof
|
|
834
851
|
criteria; it asks only when the project or outcome is genuinely ambiguous, or
|
|
835
852
|
when an irreversible or compatibility tradeoff changes what should be built.
|
|
836
853
|
Questions are grouped, carry a recommended default, and stop for reversible
|
|
@@ -984,7 +1001,7 @@ Everything below ships in 0.4.0:
|
|
|
984
1001
|
sessions, invites, and every mode they signed end together, while
|
|
985
1002
|
history stays attributed forever.
|
|
986
1003
|
- **Scout tasks** — `task add … --report` (or the "scout" checkbox, or
|
|
987
|
-
the
|
|
1004
|
+
the lead's `propose_task` with `report: true`) files a task whose
|
|
988
1005
|
deliverable is a report, never a branch: once its scope is approved,
|
|
989
1006
|
a read-only session investigates the goal as a question and hands
|
|
990
1007
|
back a title, a summary, a document, and up to five follow-ups, each
|
package/dist/action-ledger.d.ts
CHANGED
|
@@ -16,11 +16,9 @@ export type LedgerEntry = {
|
|
|
16
16
|
/** v99: what changed, in a short line (a policy's "before → after"); never a secret, prompt or body. */
|
|
17
17
|
detail: string | null;
|
|
18
18
|
};
|
|
19
|
-
/** The table as
|
|
20
|
-
export declare const LEDGER_V54_TABLE: (name: string) => string;
|
|
21
|
-
export declare const LEDGER_V54_COLUMNS: readonly ["id", "at", "actor", "repo", "task_id", "run_id", "action", "outcome", "source"];
|
|
22
|
-
/** The table as v99 made it: the v110 rebuild accepts this shape and v54's. */
|
|
19
|
+
/** The table as v99 made it (v107 has it): the one earlier shape the v110 rebuild accepts. */
|
|
23
20
|
export declare const LEDGER_V99_TABLE: (name: string) => string;
|
|
21
|
+
export declare const LEDGER_COLUMNS: readonly ["id", "at", "actor", "repo", "task_id", "run_id", "action", "outcome", "source", "detail"];
|
|
24
22
|
export declare const LEDGER_TABLE: (name: string) => string;
|
|
25
23
|
export declare const LEDGER_SCHEMA: string;
|
|
26
24
|
/** Installed after all table migrations. Work events commit with the work,
|
package/dist/action-ledger.js
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
/** v99: sign-ins (and refusals, lockouts, sign-outs) and policy changes are their own kinds of event.
|
|
2
2
|
* v110: commands a person ran on this server with their API token, from the CLI ('api') or their agent ('mcp'). */
|
|
3
3
|
export const LEDGER_SOURCES = ["work", "request", "access", "sign-in", "policy", "api", "mcp"];
|
|
4
|
-
/** The table as
|
|
5
|
-
export const LEDGER_V54_TABLE = (name) => `CREATE TABLE ${name} (
|
|
6
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
7
|
-
at TEXT NOT NULL, actor TEXT NOT NULL, repo TEXT,
|
|
8
|
-
task_id TEXT, run_id INTEGER,
|
|
9
|
-
action TEXT NOT NULL, outcome TEXT NOT NULL,
|
|
10
|
-
source TEXT NOT NULL CHECK (source IN ('work','request','access'))
|
|
11
|
-
)`;
|
|
12
|
-
export const LEDGER_V54_COLUMNS = ["id", "at", "actor", "repo", "task_id", "run_id", "action", "outcome", "source"];
|
|
13
|
-
/** The table as v99 made it: the v110 rebuild accepts this shape and v54's. */
|
|
4
|
+
/** The table as v99 made it (v107 has it): the one earlier shape the v110 rebuild accepts. */
|
|
14
5
|
export const LEDGER_V99_TABLE = (name) => `CREATE TABLE ${name} (
|
|
15
6
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16
7
|
at TEXT NOT NULL, actor TEXT NOT NULL, repo TEXT,
|
|
@@ -19,6 +10,7 @@ export const LEDGER_V99_TABLE = (name) => `CREATE TABLE ${name} (
|
|
|
19
10
|
source TEXT NOT NULL CHECK (source IN ('work','request','access','sign-in','policy')),
|
|
20
11
|
detail TEXT
|
|
21
12
|
)`;
|
|
13
|
+
export const LEDGER_COLUMNS = ["id", "at", "actor", "repo", "task_id", "run_id", "action", "outcome", "source", "detail"];
|
|
22
14
|
export const LEDGER_TABLE = (name) => `CREATE TABLE ${name} (
|
|
23
15
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
24
16
|
at TEXT NOT NULL, actor TEXT NOT NULL, repo TEXT,
|
package/dist/agent-onboard.js
CHANGED
|
@@ -86,14 +86,17 @@ export function skillFingerprint(content) {
|
|
|
86
86
|
export const RELEASED_SKILLS = [
|
|
87
87
|
"6ff9b4458f332636b6c5968c416bee4770564ef0190d62ec8640e84051fb7f60", // skills install --claude-code
|
|
88
88
|
"3fb6741eec6d394a0a41604cae321da05d85c74f440424b740151c131f125742", // the operator skill
|
|
89
|
+
"448fc51dc200b4d46c53bf191fd7ecee7c4f7487413bf4dbfb1f984a3b401993", // skills install --claude-code, with the release guide
|
|
89
90
|
];
|
|
90
91
|
const KNOWN_SKILLS = new Set([...RELEASED_SKILLS, ...[operatorSkillContent("0.0.0"), claudeCodeSkillContent()].map(skillFingerprint)]);
|
|
91
|
-
/** SHA-256 of every guide copy (console.md, operating.md, runner.md) an earlier `skills install --claude-code` wrote
|
|
92
|
+
/** SHA-256 of every guide copy (console.md, operating.md, runner.md, release.md) an earlier `skills install --claude-code` wrote
|
|
92
93
|
* beside SKILL.md, and of this build's. Only an exact one of these is removed: an edited copy is the person's. */
|
|
93
94
|
const RELEASED_GUIDES = [
|
|
94
95
|
"a5aa20f93d803fd49bbac2908e46353a4f90c95c2bb57993ee724770c28adb0b", "65fc1774c4d6dd99e71a7cfb245a61148ac74889765477ddd5066703cd28f55c", "01604f373b92de737ae7f75804c7f7f09dd690ed96a3e676be75f71358819f23",
|
|
95
96
|
"1e1ee5c46c84b65c931e86c005802478415ec026b6240367e7f12a8d66d608eb", "e5270e70eb09f202980a236a0ae36ac2e1c3df91840c24d40a6a8bd4c0d47fe8", "969dd5e97b63f27dc559889a551f4392bcf289f2b47ce802338868f06e05f1c9",
|
|
96
97
|
"bea301cb7b7d7711afb165b72c355ff829e6341abb75c59431c4a0a7c129b6cd",
|
|
98
|
+
"4b087ed3fdb60b8b7c56a2312a990eab1a854078592e355d852f4a4c5b13c752", "ac61340c17d780994052cd8ed9a0541f1fa64b80f95fc7e915e6899baa163caa", "a11a8576cc17a9e40ecfbf01503979298a4b003a4badffbd1dbd455b0eafde64",
|
|
99
|
+
"37518ca87fdbb54ff649289ec1893232137fd30c9db6e29cd1fc4bc9ba5e2f1a",
|
|
97
100
|
];
|
|
98
101
|
const guideHash = (content) => createHash("sha256").update(content).digest("hex");
|
|
99
102
|
const KNOWN_GUIDES = new Set([...RELEASED_GUIDES, ...CLAUDE_CODE_GUIDES.map(name => guideHash(claudeCodeGuideContent(name)))]);
|
|
@@ -5,21 +5,21 @@
|
|
|
5
5
|
* - The person who filed a task can't approve it ("not the requester").
|
|
6
6
|
* - Protected work (the whole project, or tasks that touch protected paths)
|
|
7
7
|
* needs two different people to approve the same exact scope, and an AI
|
|
8
|
-
*
|
|
8
|
+
* subagent or an operating mode can never decide it.
|
|
9
9
|
*
|
|
10
10
|
* The store enforces these at the one place every approval is sealed
|
|
11
11
|
* (`Store.sealScopeApproval`); this module holds the pure parts.
|
|
12
12
|
*/
|
|
13
13
|
/** Who filed a task, as far as anyone can tell: a person (browser, chat, a
|
|
14
14
|
* revision), a coordinator acting for the person who made it, an AI
|
|
15
|
-
*
|
|
15
|
+
* subagent, or automation that names no person (a schedule, the CLI, a flow
|
|
16
16
|
* with no owner). The requester rule binds only a named person. */
|
|
17
|
-
export type FilerKind = "person" | "coordinator" | "
|
|
17
|
+
export type FilerKind = "person" | "coordinator" | "subagent" | "automation";
|
|
18
18
|
export type Filer = {
|
|
19
19
|
name: string | null;
|
|
20
20
|
kind: FilerKind;
|
|
21
21
|
};
|
|
22
|
-
/** A filer from a name as the ledger writes it:
|
|
22
|
+
/** A filer from a name as the ledger writes it: a subagent's ends " (AI)"; a coordinator's starts "coordinator:"; none is automation. */
|
|
23
23
|
export declare function filerFor(name: string | null | undefined): Filer;
|
|
24
24
|
export type ApprovalRules = {
|
|
25
25
|
notRequester: boolean;
|
|
@@ -27,7 +27,7 @@ export type ApprovalRules = {
|
|
|
27
27
|
protectedPaths: string[];
|
|
28
28
|
};
|
|
29
29
|
export declare const NO_RULES: ApprovalRules;
|
|
30
|
-
/** Who is sealing: a person at a ceremony, an operating mode,
|
|
30
|
+
/** Who is sealing: a person at a ceremony, an operating mode, a subagent, or automation. */
|
|
31
31
|
export type ApproverKind = "person" | "mode" | "ai" | "automation";
|
|
32
32
|
export type ApprovalGate = {
|
|
33
33
|
verdict: "seal";
|
package/dist/approval-policy.js
CHANGED
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
* - The person who filed a task can't approve it ("not the requester").
|
|
6
6
|
* - Protected work (the whole project, or tasks that touch protected paths)
|
|
7
7
|
* needs two different people to approve the same exact scope, and an AI
|
|
8
|
-
*
|
|
8
|
+
* subagent or an operating mode can never decide it.
|
|
9
9
|
*
|
|
10
10
|
* The store enforces these at the one place every approval is sealed
|
|
11
11
|
* (`Store.sealScopeApproval`); this module holds the pure parts.
|
|
12
12
|
*/
|
|
13
|
-
/** A filer from a name as the ledger writes it:
|
|
13
|
+
/** A filer from a name as the ledger writes it: a subagent's ends " (AI)"; a coordinator's starts "coordinator:"; none is automation. */
|
|
14
14
|
export function filerFor(name) {
|
|
15
15
|
if (name == null || name === "")
|
|
16
16
|
return { name: null, kind: "automation" };
|
|
17
17
|
if (name.endsWith(" (AI)"))
|
|
18
|
-
return { name, kind: "
|
|
18
|
+
return { name, kind: "subagent" };
|
|
19
19
|
if (name.startsWith("coordinator:"))
|
|
20
20
|
return { name, kind: "coordinator" };
|
|
21
21
|
return { name, kind: "person" };
|
|
@@ -133,5 +133,5 @@ export function gateWords(gate) {
|
|
|
133
133
|
: `Your approval is recorded (${gate.have} of ${gate.need}). This work is protected, so a second person needs to approve it.`;
|
|
134
134
|
return gate.reason === "requester"
|
|
135
135
|
? "You filed this task, and this project needs someone else to approve it."
|
|
136
|
-
: "This work is protected: a person has to approve it, not an operating mode, a schedule or
|
|
136
|
+
: "This work is protected: a person has to approve it, not an operating mode, a schedule or a subagent.";
|
|
137
137
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* the identity provider); everyone else reads them. Every change is in the
|
|
5
5
|
* ledger, before → after.
|
|
6
6
|
*/
|
|
7
|
+
import { type Html } from "./html.js";
|
|
7
8
|
import type { ApprovalRules } from "./approval-policy.js";
|
|
8
9
|
export declare const APPROVAL_RULES_CSS: string;
|
|
9
10
|
export type ApprovalRulesView = {
|
|
@@ -19,7 +20,7 @@ export type ApprovalRulesView = {
|
|
|
19
20
|
};
|
|
20
21
|
/** The rules in plain words, for anyone who can see the project. */
|
|
21
22
|
export declare function rulesSummary(rules: ApprovalRules): string;
|
|
22
|
-
export declare function approvalRulesHtml(view: ApprovalRulesView,
|
|
23
|
+
export declare function approvalRulesHtml(view: ApprovalRulesView, notice: {
|
|
23
24
|
said?: string | null;
|
|
24
25
|
problem?: string | null;
|
|
25
|
-
}):
|
|
26
|
+
}): Html;
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Settings → Approval rules (v102): a project's separation of duties. An
|
|
3
|
+
* instance operator sets them (with their password, or a fresh sign-in with
|
|
4
|
+
* the identity provider); everyone else reads them. Every change is in the
|
|
5
|
+
* ledger, before → after.
|
|
6
|
+
*/
|
|
7
|
+
import { html, postForm } from "./html.js";
|
|
2
8
|
export const APPROVAL_RULES_CSS = `.approval-rules{max-width:720px;min-width:0}.approval-rules fieldset{border:0;padding:0;margin:18px 0 0;min-width:0}` +
|
|
3
9
|
`.approval-rules legend{font-weight:600;font-size:.875rem;margin-bottom:8px}.approval-rules .choice{display:grid;grid-template-columns:auto minmax(0,1fr);gap:10px;align-items:start;margin:0 0 10px;font-weight:400}` +
|
|
4
10
|
`.approval-rules .choice input{margin:.2rem 0 0}.approval-rules .choice strong{display:block;font-size:.875rem;font-weight:500}.approval-rules .choice small{display:block;color:var(--muted-foreground);font-size:.8125rem;margin-top:2px}` +
|
|
@@ -10,30 +16,19 @@ export function rulesSummary(rules) {
|
|
|
10
16
|
if (rules.notRequester)
|
|
11
17
|
parts.push("The person who filed a task can't approve it.");
|
|
12
18
|
if (rules.protectProject)
|
|
13
|
-
parts.push("All work here is protected: two people approve it, never
|
|
19
|
+
parts.push("All work here is protected: two people approve it, never a subagent or an operating mode.");
|
|
14
20
|
else if (rules.protectedPaths.length > 0)
|
|
15
|
-
parts.push(`Work touching ${rules.protectedPaths.join(", ")} is protected: two people approve it, never
|
|
21
|
+
parts.push(`Work touching ${rules.protectedPaths.join(", ")} is protected: two people approve it, never a subagent or an operating mode.`);
|
|
16
22
|
return parts.length === 0 ? "No approval rules: anyone who can approve on this project can approve its work, including their own." : parts.join(" ");
|
|
17
23
|
}
|
|
18
|
-
export function approvalRulesHtml(view,
|
|
19
|
-
const note = notice.problem ? `<p class="problem" role="alert">${
|
|
20
|
-
const changed = view.rules.updatedBy === null ? "" : ` <span class="meta">Changed by ${
|
|
21
|
-
const current = `<p class="current">${
|
|
24
|
+
export function approvalRulesHtml(view, notice) {
|
|
25
|
+
const note = notice.problem ? html `<p class="problem" role="alert">${notice.problem}</p>` : notice.said ? html `<p role="status">${notice.said}</p>` : "";
|
|
26
|
+
const changed = view.rules.updatedBy === null ? "" : html ` <span class="meta">Changed by ${view.rules.updatedBy} on ${(view.rules.updatedAt ?? "").slice(0, 10)}.</span>`;
|
|
27
|
+
const current = html `<p class="current">${rulesSummary(view.rules)}${changed}</p>`;
|
|
22
28
|
if (!view.canChange)
|
|
23
|
-
return `<section class="approval-rules">${note}${current}<p class="meta">An instance operator sets these.</p></section>`;
|
|
29
|
+
return html `<section class="approval-rules">${note}${current}<p class="meta">An instance operator sets these.</p></section>`;
|
|
24
30
|
const protect = view.rules.protectProject ? "project" : view.rules.protectedPaths.length > 0 ? "paths" : "none";
|
|
25
|
-
const radio = (value, title, hint) => `<label class="choice"><input type="radio" name="protect" value="${value}"${protect === value ?
|
|
26
|
-
const few = view.approvers < 2 ? `<p class="meta">Only ${view.approvers === 1 ? "one person" : "no one"} can approve on this project, so protected work would wait for a second approver. Add one in People first.</p>` : "";
|
|
27
|
-
return `<section class="approval-rules">${note}${current}`
|
|
28
|
-
`<form method="post" action="/settings/approval"><input type="hidden" name="csrf" value="${e(csrf)}"><input type="hidden" name="repo" value="${e(view.repo)}">` +
|
|
29
|
-
`<fieldset><legend>Who approves</legend><label class="choice"><input type="checkbox" name="not_requester" value="1"${view.rules.notRequester ? " checked" : ""}>` +
|
|
30
|
-
`<span><strong>Someone other than the requester</strong><small>The person who filed a task, or asked for a revision, can't approve it. Operating modes stop approving their signer's own filings here.</small></span></label></fieldset>` +
|
|
31
|
-
`<fieldset><legend>Protected work</legend>` +
|
|
32
|
-
radio("none", "Nothing protected", "One approval is enough.") +
|
|
33
|
-
radio("project", "The whole project", "Every task needs two different people to approve the same scope.") +
|
|
34
|
-
radio("paths", "Only these paths", "Tasks that touch them, or don't say what they touch, need two people.") +
|
|
35
|
-
`<div class="paths"><label>Protected paths, one per line<textarea name="paths" spellcheck="false" placeholder="infra/** migrations/**">${e(view.rules.protectedPaths.join("\n"))}</textarea></label></div>` +
|
|
36
|
-
few + `</fieldset>` +
|
|
37
|
-
`<div class="step-up"><label>Your Toolroll password<input type="password" name="password" autocomplete="current-password"></label></div>` +
|
|
38
|
-
`<button type="submit">Save rules</button></form></section>`;
|
|
31
|
+
const radio = (value, title, hint) => html `<label class="choice"><input type="radio" name="protect" value="${value}"${protect === value ? html ` checked` : ""}><span><strong>${title}</strong><small>${hint}</small></span></label>`;
|
|
32
|
+
const few = view.approvers < 2 ? html `<p class="meta">Only ${view.approvers === 1 ? "one person" : "no one"} can approve on this project, so protected work would wait for a second approver. Add one in People first.</p>` : "";
|
|
33
|
+
return html `<section class="approval-rules">${note}${current}${postForm("/settings/approval", html `<fieldset><legend>Who approves</legend><label class="choice"><input type="checkbox" name="not_requester" value="1"${view.rules.notRequester ? html ` checked` : ""}><span><strong>Someone other than the requester</strong><small>The person who filed a task, or asked for a revision, can't approve it. Operating modes stop approving their signer's own filings here.</small></span></label></fieldset><fieldset><legend>Protected work</legend>${radio("none", "Nothing protected", html `One approval is enough.`)}${radio("project", "The whole project", html `Every task needs two different people to approve the same scope.`)}${radio("paths", "Only these paths", html `Tasks that touch them, or don't say what they touch, need two people.`)}<div class="paths"><label>Protected paths, one per line<textarea name="paths" spellcheck="false" placeholder="infra/** migrations/**">${view.rules.protectedPaths.join("\n")}</textarea></label></div>${few}</fieldset><div class="step-up"><label>Your Toolroll password<input type="password" name="password" autocomplete="current-password"></label></div><button type="submit">Save rules</button>`, { hidden: { repo: view.repo } })}</section>`;
|
|
39
34
|
}
|
package/dist/assignment-ui.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { AssignmentSnapshot } from './assignment.js';
|
|
|
4
4
|
import type { DisplayStatus, StatusTone, WorkStatus } from './workspace-ui.js';
|
|
5
5
|
import { type AssignmentStatusOptions, type AssignmentWorkStatus } from './assignment-presentation.js';
|
|
6
6
|
import { type PullRequestFact, type TaskStatus } from './task-status.js';
|
|
7
|
+
import { type Html } from './html.js';
|
|
7
8
|
/** The existing verified receipt reader may discover damage after a verdict
|
|
8
9
|
* was saved. Keep the limitation visible without inventing another work stage. */
|
|
9
10
|
export declare function assignmentWithEvidence(assignment: AssignmentSnapshot, resultStatus: DisplayStatus | null, resultRunId?: number | null): AssignmentSnapshot;
|
|
@@ -19,7 +20,7 @@ export type AssignmentAttemptRow = {
|
|
|
19
20
|
};
|
|
20
21
|
/** Every version of the assignment, oldest first, as the card lists them. */
|
|
21
22
|
export declare function assignmentAttemptsOf(assignment: AssignmentSnapshot): AssignmentAttemptRow[];
|
|
22
|
-
export declare function assignmentAttemptsHtml(assignment: AssignmentSnapshot):
|
|
23
|
+
export declare function assignmentAttemptsHtml(assignment: AssignmentSnapshot): Html;
|
|
23
24
|
type AssignmentCardOptions = Omit<AssignmentStatusOptions, 'links'> & {
|
|
24
25
|
hideAction?: boolean;
|
|
25
26
|
problem?: boolean;
|
|
@@ -61,7 +62,7 @@ export declare function assignmentCardOf(assignment: AssignmentSnapshot, options
|
|
|
61
62
|
export declare function assignmentSummaryHtml(assignment: AssignmentSnapshot, options?: AssignmentCardOptions & {
|
|
62
63
|
compact?: boolean;
|
|
63
64
|
sentence?: string | undefined;
|
|
64
|
-
}):
|
|
65
|
+
}): Html;
|
|
65
66
|
/** Shared assignment layout; actions remain comfortable on touch screens. */
|
|
66
67
|
export declare const ASSIGNMENT_CSS = ".assignment-summary{min-width:0;overflow-wrap:anywhere}.assignment-summary .assignment-state{font-weight:600;margin:0}.assignment-summary h2.assignment-state{font-size:1.125rem;color:var(--foreground)}.assignment-summary>.assignment-attempts,.assignment-summary>.assignment-notices{border:0;box-shadow:none;padding:0;margin:.25rem 0 0;background:transparent}.assignment-summary>.button-link{margin-top:.7rem;min-height:44px}.assignment-summary>.work-action{display:inline-flex;margin-inline-start:.6rem;min-height:44px;align-items:center}.assignment-detail{margin:.55rem 0}.assignment-attempts summary,.assignment-notices summary{min-height:44px;display:list-item;align-content:center;cursor:pointer}.assignment-notices summary{font-size:.8125rem;color:var(--foreground)}.assignment-notices p{margin:.25rem 0 .5rem}.assignment-attempts ol{padding-left:1.4rem}.assignment-attempts li{margin:.4rem 0}.assignment-attempts li a{display:inline-flex;align-items:center;min-height:44px;margin-right:.7rem}.assignment-attempts li p{margin:0 0 .4rem}";
|
|
67
68
|
export {};
|
package/dist/assignment-ui.js
CHANGED
|
@@ -2,7 +2,7 @@ import { assignmentPresentationOf, shortenedMaterialReason } from './assignment-
|
|
|
2
2
|
import { plainReasonWords } from './proof.js';
|
|
3
3
|
import { chatResultHref } from './chat-controls.js';
|
|
4
4
|
import { statusDetailsHtml, statusWhyHtml } from './task-status.js';
|
|
5
|
-
|
|
5
|
+
import { html } from './html.js';
|
|
6
6
|
/** The existing verified receipt reader may discover damage after a verdict
|
|
7
7
|
* was saved. Keep the limitation visible without inventing another work stage. */
|
|
8
8
|
export function assignmentWithEvidence(assignment, resultStatus, resultRunId = assignment.receipt?.runId ?? null) {
|
|
@@ -58,8 +58,8 @@ export function assignmentAttemptsOf(assignment) {
|
|
|
58
58
|
}
|
|
59
59
|
export function assignmentAttemptsHtml(assignment) {
|
|
60
60
|
if (assignment.attempts.length === 0)
|
|
61
|
-
return
|
|
62
|
-
return `<details class="assignment-attempts"><summary>Attempts <span class="meta">${assignment.attempts.length}</span></summary><ol
|
|
61
|
+
return html ``;
|
|
62
|
+
return html `<details class="assignment-attempts"><summary>Attempts <span class="meta">${assignment.attempts.length}</span></summary><ol>${assignmentAttemptsOf(assignment).map(attempt => html `<li data-attempt-task="${attempt.taskId}" data-history-version="${attempt.taskId}"><a href="${attempt.href}">${attempt.label}</a>${attempt.runId === null ? '' : html ` <a href="/r/${attempt.runId}">Run #${attempt.runId}</a>`}${attempt.detail === null ? '' : html `<p class="meta">${attempt.detail}</p>`}</li>`)}</ol>${assignment.owner === null ? '' : html `<p class="meta">Lead: ${assignment.owner.label}${assignment.owner.active ? '' : ' · access ended'}</p>`}<p class="work-meta work-id">Task <span class="mono">${assignment.rootId}</span></p></details>`;
|
|
63
63
|
}
|
|
64
64
|
/** Only recognized storage limits and explicitly identified older records are
|
|
65
65
|
* secondary. Unknown findings and damaged current material stay in view. */
|
|
@@ -100,14 +100,7 @@ export function assignmentCardOf(assignment, options = {}) {
|
|
|
100
100
|
export function assignmentSummaryHtml(assignment, options = {}) {
|
|
101
101
|
const card = assignmentCardOf(assignment, options);
|
|
102
102
|
const tone = card.status.tone;
|
|
103
|
-
return `<section class="${options.compact ? 'assignment-summary' : 'card assignment-summary'}" aria-label="assignment progress" data-assignment="${
|
|
104
|
-
(options.compact ? `<span class="status-line" data-work-status="${card.token}" data-tone="${card.tone}"><i class="status-dot" aria-hidden="true"></i><span class="status-label">${card.label}</span></span>` : `<h2 class="assignment-state status-headline"><i aria-hidden="true"></i>${escape(card.label)}</h2>`) +
|
|
105
|
-
`<p class="meta assignment-detail status-sentence">${escape(options.sentence ?? card.status.sentence)}</p>` +
|
|
106
|
-
(card.action === null ? '' : `<a class="${options.compact ? 'work-action' : 'button-link'}" href="${escape(card.action.href)}"${card.action.openResult ? ' data-open-result' : ''}${options.compact ? '' : ' data-primary-action'}>${escape(card.action.label)}${options.compact ? ' →' : ''}</a>`) +
|
|
107
|
-
(options.compact ? '' : statusDetailsHtml(card.status)) +
|
|
108
|
-
statusWhyHtml(card.status, card.reasons, card.diagnostics) +
|
|
109
|
-
(card.notices === null ? '' : `<details class="assignment-notices"><summary>${card.notices.summary}</summary>${card.notices.lines.map(one => `<p class="meta">${escape(one)}</p>`).join('')}</details>`) +
|
|
110
|
-
assignmentAttemptsHtml(assignment) + `</section>`;
|
|
103
|
+
return html `<section class="${options.compact ? 'assignment-summary' : 'card assignment-summary'}" aria-label="assignment progress" data-assignment="${assignment.rootId}" data-work-status="${card.token}" data-tone="${card.tone}" data-headline="${card.status.headline}" data-headline-tone="${tone}"${options.compact ? '' : html ` data-task-status`}>${options.compact ? html `<span class="status-line" data-work-status="${card.token}" data-tone="${card.tone}"><i class="status-dot" aria-hidden="true"></i><span class="status-label">${card.label}</span></span>` : html `<h2 class="assignment-state status-headline"><i aria-hidden="true"></i>${card.label}</h2>`}<p class="meta assignment-detail status-sentence">${options.sentence ?? card.status.sentence}</p>${card.action === null ? '' : html `<a class="${options.compact ? 'work-action' : 'button-link'}" href="${card.action.href}"${card.action.openResult ? html ` data-open-result` : ''}${options.compact ? '' : html ` data-primary-action`}>${card.action.label}${options.compact ? ' →' : ''}</a>`}${options.compact ? '' : statusDetailsHtml(card.status)}${statusWhyHtml(card.status, card.reasons, card.diagnostics)}${card.notices === null ? '' : html `<details class="assignment-notices"><summary>${card.notices.summary}</summary>${card.notices.lines.map(one => html `<p class="meta">${one}</p>`)}</details>`}${assignmentAttemptsHtml(assignment)}</section>`;
|
|
111
104
|
}
|
|
112
105
|
/** Shared assignment layout; actions remain comfortable on touch screens. */
|
|
113
106
|
export const ASSIGNMENT_CSS = `.assignment-summary{min-width:0;overflow-wrap:anywhere}.assignment-summary .assignment-state{font-weight:600;margin:0}.assignment-summary h2.assignment-state{font-size:1.125rem;color:var(--foreground)}.assignment-summary>.assignment-attempts,.assignment-summary>.assignment-notices{border:0;box-shadow:none;padding:0;margin:.25rem 0 0;background:transparent}.assignment-summary>.button-link{margin-top:.7rem;min-height:44px}.assignment-summary>.work-action{display:inline-flex;margin-inline-start:.6rem;min-height:44px;align-items:center}.assignment-detail{margin:.55rem 0}.assignment-attempts summary,.assignment-notices summary{min-height:44px;display:list-item;align-content:center;cursor:pointer}.assignment-notices summary{font-size:.8125rem;color:var(--foreground)}.assignment-notices p{margin:.25rem 0 .5rem}.assignment-attempts ol{padding-left:1.4rem}.assignment-attempts li{margin:.4rem 0}.assignment-attempts li a{display:inline-flex;align-items:center;min-height:44px;margin-right:.7rem}.assignment-attempts li p{margin:0 0 .4rem}`;
|
package/dist/backup-ui.d.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* back up now, then the schedule. Past backups and how to restore sit in
|
|
4
4
|
* disclosures.
|
|
5
5
|
*/
|
|
6
|
+
import { type Html } from "./html.js";
|
|
6
7
|
import type { BackupRun, BackupSettings } from "./store.js";
|
|
7
8
|
/** The schedules on offer and the most copies kept. Here rather than in backup.ts so the page never loads `node:sqlite`. */
|
|
8
9
|
export declare const BACKUP_EVERY_HOURS: readonly [1, 6, 12, 24];
|
|
@@ -19,4 +20,4 @@ export type BackupView = {
|
|
|
19
20
|
export declare function backupHtml(view: BackupView, notice: {
|
|
20
21
|
said?: string | null;
|
|
21
22
|
problem?: string | null;
|
|
22
|
-
}):
|
|
23
|
+
}): Html;
|
package/dist/backup-ui.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings → Backups (sprint 8): how the last backup went and one button to
|
|
3
|
+
* back up now, then the schedule. Past backups and how to restore sit in
|
|
4
|
+
* disclosures.
|
|
5
|
+
*/
|
|
6
|
+
import { html, postForm } from "./html.js";
|
|
1
7
|
import { bytesWords } from "./storage.js";
|
|
2
8
|
import { whenUtc } from "./when-html.js";
|
|
3
9
|
/** The schedules on offer and the most copies kept. Here rather than in backup.ts so the page never loads `node:sqlite`. */
|
|
4
10
|
export const BACKUP_EVERY_HOURS = [1, 6, 12, 24];
|
|
5
11
|
export const MAX_KEEP = 100;
|
|
6
|
-
const e = (value) => String(value ?? "").replace(/[&<>"']/g, c => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
7
12
|
const when = (at) => whenUtc(at);
|
|
8
13
|
const whenWords = (at) => `${at.slice(0, 16).replace("T", " ")} UTC`;
|
|
9
14
|
const everyWords = (hours) => hours === 1 ? "Every hour" : hours === 24 ? "Every day" : `Every ${hours} hours`;
|
|
@@ -26,36 +31,20 @@ function state(view) {
|
|
|
26
31
|
: good === undefined ? "The first scheduled backup runs within a minute or two of the console starting."
|
|
27
32
|
: `Next one about ${whenWords(new Date(Date.parse(good.startedAt) + view.settings.everyHours * 3_600_000).toISOString())}.`;
|
|
28
33
|
if (last === undefined)
|
|
29
|
-
return `<h2>No backups yet</h2><p class="meta">${
|
|
34
|
+
return html `<h2>No backups yet</h2><p class="meta">${next}</p>`;
|
|
30
35
|
if (last.ok === null)
|
|
31
|
-
return `<h2>Backing up</h2><p class="meta">Started ${when(last.startedAt)}.</p>`;
|
|
36
|
+
return html `<h2>Backing up</h2><p class="meta">Started ${when(last.startedAt)}.</p>`;
|
|
32
37
|
if (last.ok === false) {
|
|
33
|
-
return `<h2 data-backup-state="failed">Last backup failed</h2><p class="problem" role="alert">${when(last.startedAt)}: ${
|
|
34
|
-
`<p class="meta">${good === undefined ? "No backup has succeeded yet." : `Last good backup ${when(good.startedAt)}.`} ${e(next)}</p>`;
|
|
38
|
+
return html `<h2 data-backup-state="failed">Last backup failed</h2><p class="problem" role="alert">${when(last.startedAt)}: ${last.error ?? "no reason was recorded"}</p><p class="meta">${good === undefined ? "No backup has succeeded yet." : html `Last good backup ${when(good.startedAt)}.`} ${next}</p>`;
|
|
35
39
|
}
|
|
36
|
-
return `<h2 data-backup-state="ok">Backed up ${when(last.startedAt)}</h2><p class="meta">${last.bytes === null ? "" : `${
|
|
40
|
+
return html `<h2 data-backup-state="ok">Backed up ${when(last.startedAt)}</h2><p class="meta">${last.bytes === null ? "" : `${bytesWords(last.bytes)} · `}${next}</p>`;
|
|
37
41
|
}
|
|
38
42
|
export function backupHtml(view, notice) {
|
|
39
43
|
const s = view.settings;
|
|
40
|
-
const note = notice.problem ? `<p class="problem" role="alert">${
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
: run.ok === false ? `<span class="failed">Failed: ${e(run.error ?? "no reason recorded")}</span>` : `<span>Running</span>`) +
|
|
47
|
-
`<span class="meta">${run.trigger === "manual" ? "by hand" : "scheduled"}</span></li>`).join("");
|
|
48
|
-
return `<section class="backups">${note}` +
|
|
49
|
-
`<div class="card backup-state"><div>${state(view)}</div>` +
|
|
50
|
-
`<form method="post" action="/settings/backups/now">${csrf}<button type="submit">Back up now</button></form></div>` +
|
|
51
|
-
`<h2>Schedule</h2>` +
|
|
52
|
-
`<form method="post" action="/settings/backups">${csrf}<fieldset>` +
|
|
53
|
-
`<div class="pair"><label>Back up<select name="every">${choices}</select></label>` +
|
|
54
|
-
`<label>Keep the newest<input type="number" name="keep" min="1" max="${MAX_KEEP}" step="1" value="${s.keep}" required></label></div>` +
|
|
55
|
-
`<label>Folder<input type="text" name="folder" value="${e(s.folder ?? "")}" placeholder="${e(view.defaultFolder)}" spellcheck="false"></label>` +
|
|
56
|
-
`<label>Your Toolroll password<input type="password" name="password" autocomplete="current-password"></label>` +
|
|
57
|
-
`</fieldset><button type="submit">Save</button></form>` +
|
|
58
|
-
`<details><summary>Recent backups${view.runs.length === 0 ? "" : ` (${view.runs.length})`}</summary>${runs === "" ? `<p class="meta">None yet.</p>` : `<ol>${runs}</ol>`}<p class="meta">In <span class="mono">${e(view.folder)}</span>. Only the database is copied: provider keys and sign-in files stay out.</p></details>` +
|
|
59
|
-
`<details><summary>Restore a backup</summary><p>Stop Toolroll, then run:</p><pre><code>toolroll restore <file> --dry-run</code></pre>` +
|
|
60
|
-
`<p class="meta">The dry run checks the backup (its version and ledger chain) and changes nothing. Run it again without <code>--dry-run</code> to restore; the current database is kept as a copy first.</p></details></section>`;
|
|
44
|
+
const note = notice.problem ? html `<p class="problem" role="alert">${notice.problem}</p>` : notice.said ? html `<p role="status">${notice.said}</p>` : "";
|
|
45
|
+
const choices = [html `<option value="off"${s.enabled ? "" : html ` selected`}>Off</option>`,
|
|
46
|
+
...BACKUP_EVERY_HOURS.map(hours => html `<option value="${hours}"${s.enabled && s.everyHours === hours ? html ` selected` : ""}>${everyWords(hours)}</option>`)];
|
|
47
|
+
const runs = view.runs.map(run => html `<li data-backup-run="${run.id}"><span>${when(run.startedAt)}</span>${run.ok === true ? html `<span>Succeeded${run.bytes === null ? "" : ` · ${bytesWords(run.bytes)}`}</span>${run.file === null ? "" : html `<span class="mono meta">${fileName(run.file)}</span>`}`
|
|
48
|
+
: run.ok === false ? html `<span class="failed">Failed: ${run.error ?? "no reason recorded"}</span>` : html `<span>Running</span>`}<span class="meta">${run.trigger === "manual" ? "by hand" : "scheduled"}</span></li>`);
|
|
49
|
+
return html `<section class="backups">${note}<div class="card backup-state"><div>${state(view)}</div>${postForm("/settings/backups/now", html `<button type="submit">Back up now</button>`)}</div><h2>Schedule</h2>${postForm("/settings/backups", html `<fieldset><div class="pair"><label>Back up<select name="every">${choices}</select></label><label>Keep the newest<input type="number" name="keep" min="1" max="${MAX_KEEP}" step="1" value="${s.keep}" required></label></div><label>Folder<input type="text" name="folder" value="${s.folder ?? ""}" placeholder="${view.defaultFolder}" spellcheck="false"></label><label>Your Toolroll password<input type="password" name="password" autocomplete="current-password"></label></fieldset><button type="submit">Save</button>`)}<details><summary>Recent backups${view.runs.length === 0 ? "" : ` (${view.runs.length})`}</summary>${runs.length === 0 ? html `<p class="meta">None yet.</p>` : html `<ol>${runs}</ol>`}<p class="meta">In <span class="mono">${view.folder}</span>. Only the database is copied: provider keys and sign-in files stay out.</p></details><details><summary>Restore a backup</summary><p>Stop Toolroll, then run:</p><pre><code>toolroll restore <file> --dry-run</code></pre><p class="meta">The dry run checks the backup (its version and ledger chain) and changes nothing. Run it again without <code>--dry-run</code> to restore; the current database is kept as a copy first.</p></details></section>`;
|
|
61
50
|
}
|
package/dist/brand-mark.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* brandIconHtml, or BrandIcon in React (browser/brand-mark.tsx).
|
|
8
8
|
*/
|
|
9
9
|
import { type BrandIconId } from "./brand-icons.js";
|
|
10
|
+
import { type Html } from "./html.js";
|
|
10
11
|
/**
|
|
11
12
|
* The icon for a service id, catalog tool name, chat app or integration key
|
|
12
13
|
* ("telegram", "agent:codex", "mcp:<repo>:sentry"), or null for a letter.
|
|
@@ -15,12 +16,12 @@ export declare function brandIconFor(name: string): BrandIconId | null;
|
|
|
15
16
|
/** The letter a mark without a logo shows. */
|
|
16
17
|
export declare function brandLetter(label: string): string;
|
|
17
18
|
/** The logo alone, decorative: an svg its surroundings size and colour. */
|
|
18
|
-
export declare function brandIconHtml(id: BrandIconId):
|
|
19
|
+
export declare function brandIconHtml(id: BrandIconId): Html;
|
|
19
20
|
/**
|
|
20
21
|
* The mark, decorative (the name always sits beside it). `name` null is a
|
|
21
22
|
* letter whatever the label: a custom tool never borrows a logo by its name.
|
|
22
23
|
*/
|
|
23
|
-
export declare function brandMarkHtml(name: string | null, label: string, connected: boolean):
|
|
24
|
+
export declare function brandMarkHtml(name: string | null, label: string, connected: boolean): Html;
|
|
24
25
|
/** Where the integration tiles (Settings → Tools) switch to their phone layout, and the mark with them. */
|
|
25
26
|
export declare const TILE_PHONE = "@media(max-width:600px)";
|
|
26
27
|
/** The mark's own sizes; `--brand-mark` is its side, for layouts that line up beside it. */
|
package/dist/brand-mark.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* brandIconHtml, or BrandIcon in React (browser/brand-mark.tsx).
|
|
8
8
|
*/
|
|
9
9
|
import { BRAND_ICONS } from "./brand-icons.js";
|
|
10
|
+
import { html } from "./html.js";
|
|
10
11
|
/** Service ids, catalog tool names and integration keys that differ from their icon's id. */
|
|
11
12
|
const ALIASES = {
|
|
12
13
|
atlassian: "jira", "chrome-devtools": "chrome", codex: "openai", "microsoft-teams": "teams", "better-stack": "betterstack",
|
|
@@ -25,11 +26,10 @@ export function brandIconFor(name) {
|
|
|
25
26
|
export function brandLetter(label) {
|
|
26
27
|
return (label.trim().match(/[\p{L}\p{N}]/u)?.[0] ?? "?").toUpperCase();
|
|
27
28
|
}
|
|
28
|
-
const e = (value) => value.replace(/[&<>"']/g, c => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
29
29
|
/** The logo alone, decorative: an svg its surroundings size and colour. */
|
|
30
30
|
export function brandIconHtml(id) {
|
|
31
31
|
const icon = BRAND_ICONS[id];
|
|
32
|
-
return `<svg viewBox="0 0 24 24" focusable="false" aria-hidden="true"><path fill="currentColor"${icon.evenodd
|
|
32
|
+
return html `<svg viewBox="0 0 24 24" focusable="false" aria-hidden="true"><path fill="currentColor"${icon.evenodd && html ` fill-rule="evenodd"`} d="${icon.path}"/></svg>`;
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
35
|
* The mark, decorative (the name always sits beside it). `name` null is a
|
|
@@ -37,8 +37,8 @@ export function brandIconHtml(id) {
|
|
|
37
37
|
*/
|
|
38
38
|
export function brandMarkHtml(name, label, connected) {
|
|
39
39
|
const icon = name === null ? null : brandIconFor(name);
|
|
40
|
-
const inside = icon === null ?
|
|
41
|
-
return `<span class="brand-mark" data-connected="${connected}"${icon === null
|
|
40
|
+
const inside = icon === null ? brandLetter(label) : brandIconHtml(icon);
|
|
41
|
+
return html `<span class="brand-mark" data-connected="${String(connected)}"${icon === null && html ` data-letter`} aria-hidden="true">${inside}</span>`;
|
|
42
42
|
}
|
|
43
43
|
/** Where the integration tiles (Settings → Tools) switch to their phone layout, and the mark with them. */
|
|
44
44
|
export const TILE_PHONE = "@media(max-width:600px)";
|