toolroll 0.9.72 → 0.9.74
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 +108 -0
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/agent-fence.d.ts +5 -0
- package/dist/agent-fence.js +42 -7
- package/dist/agent-turns.js +5 -0
- package/dist/agents-live.d.ts +31 -0
- package/dist/agents-live.js +43 -0
- package/dist/browser/THIRD_PARTY_NOTICES.txt +40 -0
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +31 -30
- package/dist/browser-workspace.d.ts +6 -0
- package/dist/builder.js +1 -1
- package/dist/chat-actions.js +13 -8
- package/dist/chat-continuity.js +10 -0
- package/dist/chat-decide.d.ts +47 -1
- package/dist/chat-decide.js +170 -4
- package/dist/chat-quiet.js +3 -3
- package/dist/check-report.js +2 -1
- package/dist/coding-provider.js +4 -2
- package/dist/commit-extract.d.ts +17 -0
- package/dist/commit-extract.js +82 -4
- package/dist/contracts/console-api.d.ts +3 -3
- package/dist/contracts/console-api.js +4 -4
- package/dist/contracts/release-terms.d.ts +7 -1
- package/dist/contracts/release-terms.js +6 -2
- package/dist/deploy-gate.js +5 -2
- package/dist/desktop-update.d.ts +1 -1
- package/dist/dispose.js +35 -2
- package/dist/exec.d.ts +1 -1
- package/dist/exec.js +20 -19
- package/dist/first-run.d.ts +17 -1
- package/dist/first-run.js +38 -0
- package/dist/flow-gallery-ui.js +4 -2
- package/dist/flow-gallery.d.ts +3 -0
- package/dist/flow-gallery.js +6 -2
- package/dist/flow-triggers.d.ts +8 -0
- package/dist/flow-triggers.js +15 -0
- package/dist/flows-cli.js +13 -3
- package/dist/guides.js +10 -1
- package/dist/invoke.d.ts +13 -0
- package/dist/invoke.js +21 -12
- package/dist/kits-ui.js +4 -2
- package/dist/kits.d.ts +6 -4
- package/dist/kits.js +9 -11
- package/dist/knowledge-ui.d.ts +4 -1
- package/dist/knowledge-ui.js +10 -5
- package/dist/lead-progress.d.ts +4 -1
- package/dist/lead-progress.js +3 -1
- package/dist/lead.js +4 -0
- package/dist/live-bus.d.ts +2 -0
- package/dist/live-bus.js +1 -0
- package/dist/live-children-read.d.ts +52 -0
- package/dist/live-children-read.js +333 -0
- package/dist/live-children.d.ts +73 -0
- package/dist/live-children.js +318 -0
- package/dist/live-detail.d.ts +4 -3
- package/dist/live-detail.js +9 -5
- package/dist/live-input.js +7 -8
- package/dist/live-main-read.d.ts +19 -0
- package/dist/live-main-read.js +91 -0
- package/dist/live-open.d.ts +28 -0
- package/dist/live-open.js +125 -0
- package/dist/live-records.d.ts +10 -1
- package/dist/live-records.js +16 -5
- package/dist/live.d.ts +54 -22
- package/dist/live.js +218 -69
- package/dist/mcp-connect.d.ts +10 -0
- package/dist/mcp-connect.js +2 -0
- package/dist/operate.js +3 -2
- package/dist/peek-cli.d.ts +10 -3
- package/dist/peek-cli.js +17 -8
- package/dist/planner.js +1 -1
- package/dist/project-tools.js +3 -1
- package/dist/protected-storage.d.ts +67 -0
- package/dist/protected-storage.js +398 -0
- package/dist/provider-limits.d.ts +3 -1
- package/dist/provider-limits.js +5 -2
- package/dist/provider.js +6 -12
- package/dist/release-adapters.d.ts +8 -1
- package/dist/release-adapters.js +84 -14
- package/dist/release-authority.d.ts +6 -0
- package/dist/release-authority.js +9 -0
- package/dist/release-card.d.ts +76 -0
- package/dist/release-card.js +252 -0
- package/dist/release-carry.d.ts +3 -0
- package/dist/release-carry.js +23 -4
- package/dist/release-cli.js +36 -3
- package/dist/release-git.d.ts +43 -0
- package/dist/release-git.js +106 -0
- package/dist/release.d.ts +11 -2
- package/dist/release.js +82 -6
- package/dist/repository-context-ui.js +2 -1
- package/dist/run-efficiency.d.ts +3 -3
- package/dist/run-efficiency.js +20 -8
- package/dist/scope.d.ts +7 -0
- package/dist/scope.js +22 -5
- package/dist/scout.d.ts +3 -0
- package/dist/scout.js +5 -3
- package/dist/serve.js +34 -23
- package/dist/server/agents-live-lead.d.ts +47 -0
- package/dist/server/agents-live-lead.js +116 -0
- package/dist/server/agents-live-room.d.ts +23 -0
- package/dist/server/agents-live-room.js +88 -0
- package/dist/server/chat.js +84 -30
- package/dist/server/chrome.js +2 -2
- package/dist/server/console.css +68 -40
- package/dist/server/flows.js +40 -10
- package/dist/server/live.d.ts +4 -0
- package/dist/server/live.js +42 -2
- package/dist/server/pages.js +81 -1
- package/dist/server/render-chat.d.ts +15 -1
- package/dist/server/render-chat.js +14 -7
- package/dist/server/render-pages.d.ts +8 -4
- package/dist/server/render-pages.js +55 -50
- package/dist/server/render-settings.js +1 -1
- package/dist/server/render-tasks.js +10 -3
- package/dist/server/route-table.js +5 -1
- package/dist/server/runtime.d.ts +1 -1
- package/dist/server/settings.js +29 -8
- package/dist/server/task-views.js +1 -1
- package/dist/server/tasks.js +59 -41
- package/dist/setup-guide.d.ts +2 -2
- package/dist/store.d.ts +34 -1
- package/dist/store.js +81 -2
- package/dist/stream-scope.d.ts +18 -0
- package/dist/stream-scope.js +28 -0
- package/dist/subagent-memory.d.ts +1 -0
- package/dist/subagent-memory.js +1 -1
- package/dist/subagent-tools.d.ts +22 -6
- package/dist/subagent-tools.js +44 -8
- package/dist/subagent-work.d.ts +2 -0
- package/dist/subagent-work.js +5 -2
- package/dist/subagents-ui.js +6 -4
- package/dist/subscription-chat.js +5 -3
- package/dist/telegram.d.ts +6 -1
- package/dist/telegram.js +58 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,113 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.74 — 2026-10-27
|
|
4
|
+
|
|
5
|
+
- **Agents → Live: every agent and subagent at a glance.** A read-only grid
|
|
6
|
+
of everything running, on desktop and phone: what each agent is doing
|
|
7
|
+
right now, how long it has run and its latest lines. Subagents get their
|
|
8
|
+
own tiles nested under their parent; they appear when they start and show
|
|
9
|
+
Done for 30 seconds before leaving. A quiet agent reads **Needs you** with
|
|
10
|
+
a thin amber outline. Click a tile's title to open it full screen (Escape
|
|
11
|
+
brings you back). Your own lead chats show as lead tiles. Nothing on the
|
|
12
|
+
page acts, and the grid holds still while you look at it. Terminals you
|
|
13
|
+
drive yourself stay off the grid; one line links to Live terminals. Only a
|
|
14
|
+
signed-in browser can read it: tokens are refused.
|
|
15
|
+
- **Visiting a page starts nothing.** Opening Chat, a task, a result or a
|
|
16
|
+
project shows the page with a ready composer; your first message starts
|
|
17
|
+
the conversation. A retry or double submit lands in the conversation it
|
|
18
|
+
started. Link previews and prefetches can't start one.
|
|
19
|
+
- **A cleaner console.** One status chip everywhere, framed panels on the
|
|
20
|
+
Board, Settings, Knowledge and Projects, and a road on every task (Plan →
|
|
21
|
+
You approve → Build → Checks → Ready) built only from what happened.
|
|
22
|
+
Attempts show as a timeline and warnings as plain notices. Amber now means
|
|
23
|
+
only "waits on you". Built from ReUI's free, MIT-licensed components
|
|
24
|
+
(credited in THIRD_PARTY_NOTICES.md).
|
|
25
|
+
- **The old task list is retired.** `/tasks` now opens Work, which gained a
|
|
26
|
+
state filter (`/work?state=`), templates (`/tasks/new?template=`) and keeps
|
|
27
|
+
your draft when a filing is refused.
|
|
28
|
+
- **Rerunning a failed release check keeps your approval.**
|
|
29
|
+
`toolroll task regate` queues the same approved commit again instead of
|
|
30
|
+
re-filing it, and `toolroll release` waits for the rerun and uses it. A
|
|
31
|
+
changed commit, scope or approval still refuses.
|
|
32
|
+
- Database: schema v136 (each run's "Show commands" answer is kept for the
|
|
33
|
+
live view).
|
|
34
|
+
|
|
35
|
+
## 0.9.73 — 2026-10-25
|
|
36
|
+
|
|
37
|
+
- **Carry release approvals when checks aren't sandboxed.** A project whose
|
|
38
|
+
checks run outside the sandbox (like Toolroll's own release project) can
|
|
39
|
+
let later test-only commits carry a release approval:
|
|
40
|
+
`toolroll release unsandboxed-carry on --repo <path> --as <you> --token
|
|
41
|
+
<password>`. It is off by default; only a person turns it on, and it counts
|
|
42
|
+
only for releases approved while it is on. The approval sheet says plainly
|
|
43
|
+
that those later commits will run outside the sandbox without being shown
|
|
44
|
+
to you first. Every other carry rule stays: tests and docs only, the
|
|
45
|
+
400-line, 20-file and 800-line-churn limits, an identical production build,
|
|
46
|
+
and main's open nightly failures. Turning it off, or checks that reach
|
|
47
|
+
further than at the approval, stops carrying.
|
|
48
|
+
- **`toolroll release` stops at the gate when the pull request's checks
|
|
49
|
+
fail.** Before it files the release task or asks for your approval, it
|
|
50
|
+
reads the required checks on the exact commit. One that failed or was
|
|
51
|
+
cancelled stops the release with the failing job names, so you aren't
|
|
52
|
+
asked to approve a commit that can't merge. Checks not started or still
|
|
53
|
+
running are named and waited for; if they don't finish within the gate's
|
|
54
|
+
limit (60 minutes, `--limit gate=<min>`), it stops there too. The ci step
|
|
55
|
+
still checks every one again before merging.
|
|
56
|
+
- **Chat apps, browsers and password managers are off limits to every
|
|
57
|
+
launch.** Builds, checks, flows, tool servers and coding sessions can't
|
|
58
|
+
read or write where Slack, Telegram, Discord, Teams, WhatsApp, 1Password,
|
|
59
|
+
Bitwarden, or the Chrome, Chromium, Brave, Edge, Arc, Firefox, Zen,
|
|
60
|
+
Vivaldi, Opera, Orion and Safari profiles keep their data, on macOS and
|
|
61
|
+
Linux, under both sandbox versions and in Codex's sandbox. This covers the
|
|
62
|
+
folders by name and by real path, links inside them, and a moved profile
|
|
63
|
+
or config folder named in the environment. No project setting reopens
|
|
64
|
+
them, and those apps' variables (SLACK_, TELEGRAM_, DISCORD_, TEAMS_, OP_,
|
|
65
|
+
BW_, BITWARDENCLI_) are removed from every launch; a tool server keeps the
|
|
66
|
+
secrets it names.
|
|
67
|
+
- **`toolroll release` runs Git safely on the release checkout.** The release
|
|
68
|
+
used to run plain Git there, so a setting planted in the checkout's Git
|
|
69
|
+
config (an include, a hook folder, an ssh command, a proxy, a URL rewrite
|
|
70
|
+
and the like) could run a command or send the gh token elsewhere. Now it
|
|
71
|
+
uses the repository and GitHub URL from Toolroll's own records, runs Git,
|
|
72
|
+
gh and ssh by absolute path with a fixed environment, pins HTTPS settings
|
|
73
|
+
on the command line, and allows only the plain settings Git writes itself.
|
|
74
|
+
Any other setting stops the release and names it. The checkout must be a
|
|
75
|
+
project Toolroll records, with a recorded remote.
|
|
76
|
+
- **Widening actions take your password.** Setting the permission default to
|
|
77
|
+
Full access, giving a subagent a tool, loosening a tool rule, accepting a
|
|
78
|
+
subagent's suggested looser rule, and adding a trigger that brings in work
|
|
79
|
+
from anyone (GitHub from anyone, mail from any sender, from the Triggers
|
|
80
|
+
panel, a gallery template or a starter kit) now ask for your password (a
|
|
81
|
+
signed-in session does under Passwordless). Narrowing needs none. A chat
|
|
82
|
+
card, import or template can no longer do any of these; it points to the
|
|
83
|
+
page that can.
|
|
84
|
+
- **Approving a release from chat shows the whole release first.** A tap
|
|
85
|
+
approves a deploy only through the release card: every signed term and
|
|
86
|
+
release term, one line each (it says so when later test-only commits
|
|
87
|
+
will run outside the sandbox), and the commits since the last release
|
|
88
|
+
Toolroll recorded as merged. Continue sends the terms (as a file where the
|
|
89
|
+
chat app allows), and Approve is bound to exactly what was shown;
|
|
90
|
+
anything changed in between refuses with the new card. It works only in
|
|
91
|
+
your own private chat (Telegram or a Slack direct message); a group,
|
|
92
|
+
someone else's chat, Discord and Teams are refused. Each such approval is
|
|
93
|
+
announced to you, naming the chat app.
|
|
94
|
+
- **Network, writes, check access and sandbox settings stay on the typed
|
|
95
|
+
password under Passwordless,** and a test now fails if one moves to the
|
|
96
|
+
ordinary signed-in step-up.
|
|
97
|
+
- **Launches don't wait on macOS's "access data from other apps" prompt.**
|
|
98
|
+
Toolroll looks inside other apps' containers only after a 3-second look in
|
|
99
|
+
a separate process shows macOS lets it, so a service run by launchd no
|
|
100
|
+
longer hangs every fenced launch. While macOS is still asking, the
|
|
101
|
+
containers stay denied by name and real path, and the links found by the
|
|
102
|
+
last full look stay denied; a look that fails for any other reason refuses
|
|
103
|
+
the launch.
|
|
104
|
+
- **The release check ends itself before its time limit,** stopping the
|
|
105
|
+
parts still running and keeping every log and its report, and a failed
|
|
106
|
+
part's log reaches the run's output as soon as it fails.
|
|
107
|
+
- **`toolroll release` rides out short GitHub network timeouts** while a
|
|
108
|
+
step waits, asking again until that step's own limit.
|
|
109
|
+
- No database change: the schema stays at v135.
|
|
110
|
+
|
|
3
111
|
## 0.9.72 — 2026-10-25
|
|
4
112
|
|
|
5
113
|
- **One approval per release.** A person approves a release once. Later
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -321,6 +321,46 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
321
321
|
SOFTWARE.
|
|
322
322
|
|
|
323
323
|
|
|
324
|
+
## ReUI — MIT
|
|
325
|
+
|
|
326
|
+
Components in `src/browser/components/ui/` adapt free, MIT-licensed ReUI
|
|
327
|
+
primitives and examples (the Radix base) from:
|
|
328
|
+
https://github.com/keenthemes/reui/tree/067aa0d/registry-reui/bases/radix/reui
|
|
329
|
+
|
|
330
|
+
Source inspected 27 October 2026, revision `067aa0d`. Files copied by hand (the
|
|
331
|
+
shadcn CLI and ReUI's registry CSS variables are not used) and cut to Toolroll's
|
|
332
|
+
tokens:
|
|
333
|
+
|
|
334
|
+
- `badge.tsx` — the light badge and its status-dot example (`c-badge-22`): one
|
|
335
|
+
20px chip size and a decorative dot on the existing tones, plus a "Needs you"
|
|
336
|
+
tone. The style variants, solid fills and focus/invert tones are omitted.
|
|
337
|
+
- `frame.tsx` — the well of panels: one geometry (a raised well, hairline,
|
|
338
|
+
concentric panel corners) with the style variants, spacing scale, stacking
|
|
339
|
+
modes and panel shadows omitted; `FramePanel` can render as its child.
|
|
340
|
+
- `alert.tsx` — cut to a neutral and a destructive tone with an icon column;
|
|
341
|
+
the info, warning, success and invert tones, the title/action slots and the
|
|
342
|
+
style variants are omitted.
|
|
343
|
+
- `timeline.tsx` — cut to a read-only vertical list (item, indicator on a
|
|
344
|
+
hairline rail, content, date); the active-step context, horizontal mode and
|
|
345
|
+
styled indicators are omitted.
|
|
346
|
+
- `stepper.tsx` — its read-only horizontal layout (indicator, separator,
|
|
347
|
+
title, `aria-current="step"`) is followed by the task page's road
|
|
348
|
+
(`src/browser/first-run.tsx` `Journey`); no stepper source is copied, and
|
|
349
|
+
the controlled state, keyboard navigation and triggers are omitted.
|
|
350
|
+
|
|
351
|
+
No ReUI Pro blocks, icons or templates are included.
|
|
352
|
+
|
|
353
|
+
MIT License
|
|
354
|
+
|
|
355
|
+
Copyright (c) 2025 Keenthemes Inc
|
|
356
|
+
|
|
357
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
358
|
+
|
|
359
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
360
|
+
|
|
361
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
362
|
+
|
|
363
|
+
|
|
324
364
|
## Integration logos: Simple Icons (CC0 1.0) and svgl (MIT)
|
|
325
365
|
|
|
326
366
|
The integration logos in `src/brand-icons.ts` are generated by
|
package/dist/agent-fence.d.ts
CHANGED
|
@@ -29,6 +29,8 @@ export declare function agentFence(options: {
|
|
|
29
29
|
boundary?: TaskBoundary | null;
|
|
30
30
|
binding?: WorktreeBinding | null;
|
|
31
31
|
runFolders?: RunFolders;
|
|
32
|
+
/** Protected storage as this launch resolved it (protected-storage.ts); resolved here when not given. */
|
|
33
|
+
protectedStorage?: readonly string[];
|
|
32
34
|
}): string[];
|
|
33
35
|
/** The task's boundary: `shell` joins the OS fence; `fileTools` only Claude's file-tool deny rules; `readOnly` and
|
|
34
36
|
* `lookup` are other tasks' git metadata and checkout roots (see FenceGrants). */
|
|
@@ -103,6 +105,9 @@ export type FenceGrants = {
|
|
|
103
105
|
signIns?: SignInGrants;
|
|
104
106
|
/** Never written, wherever the launch writes (homeWriteDenies): what runs for the operator next or signs them in. */
|
|
105
107
|
writeDenies?: string[];
|
|
108
|
+
/** Protected storage (protected-storage.ts): denied to read and write by every backend, and no grant equal to or
|
|
109
|
+
* inside one survives (a sandbox that lets the more specific path win can't reopen it). */
|
|
110
|
+
protectedStorage?: string[];
|
|
106
111
|
};
|
|
107
112
|
export { isWritesMode, WRITES_DEFAULT, type WritesMode } from "./writes-mode.js";
|
|
108
113
|
/**
|
package/dist/agent-fence.js
CHANGED
|
@@ -34,6 +34,7 @@ import { readMonitoring } from "./monitoring-settings.js";
|
|
|
34
34
|
import { existingFolders } from "./names.js";
|
|
35
35
|
import { DEPS_FOLDER, linkedKey, readyCopy, sharedCopies } from "./shared-deps.js";
|
|
36
36
|
import { SEATBELT_PROBE, sandboxRuntimeProblem, srtSpawn } from "./sandbox-runtime.js";
|
|
37
|
+
import { protectedStorageDenies, protectedStorageNames } from "./protected-storage.js";
|
|
37
38
|
import { getDefaultWritePaths } from "@anthropic-ai/sandbox-runtime";
|
|
38
39
|
/** Names that are secrets wherever the database lives (used when its folder is shared with other things). */
|
|
39
40
|
const SENSITIVE_NAME = /(^up-login\.txt$|token|secret|login|password|credential|vapid|keys?\b|\.pem$|\.key$|^backups$|^evidence$|^remote$|^sign-in\.json$|^monitoring\.json$)/i;
|
|
@@ -188,11 +189,18 @@ export function agentFence(options) {
|
|
|
188
189
|
for (const path of options.boundary?.protectedShell ?? [])
|
|
189
190
|
fence.add(real(path));
|
|
190
191
|
const protectedPaths = new Set([...fence].flatMap(path => path === runFolder ? [path, real(path)] : [real(path)]));
|
|
192
|
+
// Protected storage (chat apps, browser profiles, password managers): every launch, every policy, never exemptible,
|
|
193
|
+
// kept by the name it was resolved under as well as where it leads.
|
|
194
|
+
const storage = options.protectedStorage ?? protectedStorageDenies(home);
|
|
195
|
+
for (const path of storage) {
|
|
196
|
+
protectedPaths.add(path);
|
|
197
|
+
protectedPaths.add(real(path));
|
|
198
|
+
}
|
|
191
199
|
for (const path of options.boundary?.shell ?? [])
|
|
192
200
|
fence.add(path);
|
|
193
201
|
const binding = options.binding;
|
|
194
202
|
const needed = binding === undefined || binding === null ? [] : [binding.gitdir, real(join(binding.main, ".git"))];
|
|
195
|
-
return [...new Set([...fence].flatMap(path => path === runFolder ? [path, real(path)] : [real(path)]))].filter(path => {
|
|
203
|
+
return [...new Set([...[...fence].flatMap(path => path === runFolder ? [path, real(path)] : [real(path)]), ...storage.flatMap(path => [path, real(path)])])].filter(path => {
|
|
196
204
|
if (protectedPaths.has(path) || !needed.some(target => target === path || inside(target, path)))
|
|
197
205
|
return true;
|
|
198
206
|
console.warn(`Dropped build fence entry covering this checkout's own Git data: ${path}`);
|
|
@@ -237,10 +245,12 @@ export function taskBoundary(options) {
|
|
|
237
245
|
const present = (names) => names.map(name => join(home, name)).filter(path => existsSync(path)).map(real);
|
|
238
246
|
// Policy v2 (docs/plans/sandbox-parity.md §1.2): the home folder, the repositories beside this one and the project's
|
|
239
247
|
// primary checkout are read as Anthropic's sandbox runtime reads them; only writes to the primary checkout stay denied.
|
|
248
|
+
// Protected storage by name, under both policies (protected-storage.ts): never part of the v1-only home configuration.
|
|
249
|
+
const storage = protectedStorageNames(home);
|
|
240
250
|
if ((options.policy ?? 1) === 2)
|
|
241
|
-
return v2Boundary(worktree, main, gitdir);
|
|
251
|
+
return v2Boundary(worktree, main, gitdir, storage);
|
|
242
252
|
const shell = new Set(present(HOME_CONFIGURATION.filter(name => !own.has(name))));
|
|
243
|
-
const protectedShell = [...shell];
|
|
253
|
+
const protectedShell = [...shell, ...storage];
|
|
244
254
|
const repository = (path) => existsSync(join(path, ".git"));
|
|
245
255
|
for (const parent of new Set([dirname(worktree), ...(main === null ? [] : [dirname(main)])])) {
|
|
246
256
|
// Home contains toolchain clones such as .nvm and .oh-my-zsh. The
|
|
@@ -290,7 +300,7 @@ export function taskBoundary(options) {
|
|
|
290
300
|
/** A v2 boundary: nothing of the home folder, the repositories beside this one or the primary checkout is denied to
|
|
291
301
|
* read; the primary checkout (all but its `.git`, which this worktree's Git needs) is never written; other tasks' Git
|
|
292
302
|
* metadata is read, never written, as under v1. */
|
|
293
|
-
function v2Boundary(worktree, main, gitdir) {
|
|
303
|
+
function v2Boundary(worktree, main, gitdir, storage) {
|
|
294
304
|
const noWrite = new Set();
|
|
295
305
|
if (main !== null && main !== worktree) {
|
|
296
306
|
let entries = [];
|
|
@@ -309,7 +319,7 @@ function v2Boundary(worktree, main, gitdir) {
|
|
|
309
319
|
const { readOnly, lookup } = siblingGitData(main, gitdir);
|
|
310
320
|
for (const path of noWrite)
|
|
311
321
|
readOnly.add(path);
|
|
312
|
-
return { protectedShell: [], shell: [], fileTools: [], readOnly: [...readOnly].sort(), lookup: [...lookup].sort(), homeNames: [] };
|
|
322
|
+
return { protectedShell: [...storage], shell: [], fileTools: [], readOnly: [...readOnly].sort(), lookup: [...lookup].sort(), homeNames: [] };
|
|
313
323
|
}
|
|
314
324
|
/**
|
|
315
325
|
* Git needs this worktree's metadata and the shared objects/refs, but must not prune or rewrite the metadata of another
|
|
@@ -765,6 +775,18 @@ function codexFilesystem(fence, fullAccess, grants) {
|
|
|
765
775
|
if (basename(path) === INSTANCE_DIR)
|
|
766
776
|
access.set(path, "deny");
|
|
767
777
|
}
|
|
778
|
+
// Protected storage: denied by name and real path, and nothing granted inside it (Codex lets the more specific path
|
|
779
|
+
// win, so a grant there would reopen it).
|
|
780
|
+
const storage = grants.protectedStorage ?? [];
|
|
781
|
+
if (storage.length > 0) {
|
|
782
|
+
for (const [path, mode] of [...access])
|
|
783
|
+
if (mode !== "deny" && storage.some(one => path === one || inside(path, one)))
|
|
784
|
+
access.delete(path);
|
|
785
|
+
for (const path of storage) {
|
|
786
|
+
access.set(path, "deny");
|
|
787
|
+
access.set(real(path), "deny");
|
|
788
|
+
}
|
|
789
|
+
}
|
|
768
790
|
return access;
|
|
769
791
|
}
|
|
770
792
|
/**
|
|
@@ -971,6 +993,16 @@ export function srtTermsOf(fence, grants = NO_GRANTS) {
|
|
|
971
993
|
denyWrite.add(sealed);
|
|
972
994
|
}
|
|
973
995
|
}
|
|
996
|
+
// Protected storage last: denied to read and write by name and real path, and no read grant inside it survives.
|
|
997
|
+
const storage = grants.protectedStorage ?? [];
|
|
998
|
+
const isProtected = (path) => storage.some(one => path === one || inside(path, one) || real(path) === one || inside(real(path), one));
|
|
999
|
+
for (const path of storage) {
|
|
1000
|
+
denyRead.add(path);
|
|
1001
|
+
denyWrite.add(path);
|
|
1002
|
+
}
|
|
1003
|
+
for (const path of [...allowRead])
|
|
1004
|
+
if (isProtected(path))
|
|
1005
|
+
allowRead.delete(path);
|
|
974
1006
|
const readConfig = { denyOnly: [...denyRead].sort(), allowWithinDeny: [...allowRead].sort() };
|
|
975
1007
|
const writes = grants.writes;
|
|
976
1008
|
// A sign-ins check also writes Codex's session and log folders and its own Claude transcripts (never inside a
|
|
@@ -983,7 +1015,7 @@ export function srtTermsOf(fence, grants = NO_GRANTS) {
|
|
|
983
1015
|
const network = grants.network;
|
|
984
1016
|
return {
|
|
985
1017
|
readConfig,
|
|
986
|
-
writeConfig: { allowOnly: [...new Set(allowOnly)].sort(), denyWithinAllow: [...denyWrite].sort() },
|
|
1018
|
+
writeConfig: { allowOnly: [...new Set(allowOnly)].filter(path => path === "/" || !isProtected(path.replace(/\*$/, ""))).sort(), denyWithinAllow: [...denyWrite].sort() },
|
|
987
1019
|
allowMachLookup: [...grants.machLookup ?? [], ...grants.signIns?.mach ?? []],
|
|
988
1020
|
...(grants.browser === true ? { browser: true } : {}),
|
|
989
1021
|
...(grants.keychain === true ? { keychain: true } : {}),
|
|
@@ -1078,7 +1110,9 @@ export function launchSandboxOf(profile) {
|
|
|
1078
1110
|
export function fenceFor(kind, ctx) {
|
|
1079
1111
|
const home = ctx.home === undefined ? {} : { home: ctx.home };
|
|
1080
1112
|
// The project's read denies join the fence by name, there or not (the runtime denies a path that isn't there yet).
|
|
1081
|
-
|
|
1113
|
+
// Protected storage, resolved once for this launch, from the plane's own environment (protected-storage.ts).
|
|
1114
|
+
const storage = protectedStorageDenies(ctx.home ?? homedir());
|
|
1115
|
+
const fence = agentFence({ databaseFile: ctx.databaseFile, worktree: ctx.worktree, ...home, boundary: ctx.boundary, binding: ctx.binding, runFolders: ctx.runFolders, extra: [...ctx.extra ?? [], ...ctx.readDeny ?? []], protectedStorage: storage });
|
|
1082
1116
|
const found = fenceGrants({ databaseFile: ctx.databaseFile, worktree: ctx.worktree, boundary: ctx.boundary, runFolders: ctx.runFolders, linked: ctx.linked ?? null, binding: ctx.binding, ...home, poolRoots: ctx.poolRoots });
|
|
1083
1117
|
// Toolroll's own install, the sandbox runtime's package and the Node running them, never written by anything it
|
|
1084
1118
|
// launches: rewriting the code that fences a build would switch the fence off.
|
|
@@ -1095,6 +1129,7 @@ export function fenceFor(kind, ctx) {
|
|
|
1095
1129
|
...(kind === "build" ? { keychain: true } : {}),
|
|
1096
1130
|
// Under a write allowlist these are out of reach already; where the launch writes anywhere, they are denied by name.
|
|
1097
1131
|
writeDenies: homeWriteDenies(ctx.home ?? homedir(), ctx.binding, ctx.provider ?? null),
|
|
1132
|
+
protectedStorage: storage,
|
|
1098
1133
|
...(made.pool === undefined ? {} : { pool: { ...made.pool, ...(ctx.policy === 2 ? { readable: true } : {}),
|
|
1099
1134
|
// Flows' scratch folders in the pool (and any the caller names) stay denied whole, at every policy version.
|
|
1100
1135
|
sealed: sealedScratch(ctx.poolRoots, ctx.scratchRoots ?? []) } }),
|
package/dist/agent-turns.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import { topLevelEvent } from "./stream-scope.js";
|
|
1
2
|
/** What one stream event says about the turn; null when it says nothing. */
|
|
2
3
|
export function turnSignalOf(event) {
|
|
3
4
|
const type = typeof event["type"] === "string" ? event["type"] : "";
|
|
5
|
+
// A subagent's events are not the parent's turn: its result never marks the parent idle or clears its open tools,
|
|
6
|
+
// and its init or words never mark it busy (stream-scope.ts).
|
|
7
|
+
if (!topLevelEvent(event))
|
|
8
|
+
return null;
|
|
4
9
|
switch (type) {
|
|
5
10
|
// Claude stream-json: the session starts, the assistant speaks or calls a tool, a tool answers; then one result.
|
|
6
11
|
case "system": return event["subtype"] === "init" ? "busy" : null;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type AgentsLiveRun, type Store } from "./store.js";
|
|
2
|
+
/** An ended agent stays on the grid this long, then leaves (it remains on the List). */
|
|
3
|
+
export declare const LIVE_ENDED_KEEP_MS: number;
|
|
4
|
+
export type AgentLiveState = "working" | "needs-you" | "done" | "failed" | "stopped" | "parked";
|
|
5
|
+
export type AgentLiveTile = {
|
|
6
|
+
runId: number;
|
|
7
|
+
role: AgentsLiveRun["role"];
|
|
8
|
+
taskId: string;
|
|
9
|
+
title: string;
|
|
10
|
+
project: string | null;
|
|
11
|
+
agent: string;
|
|
12
|
+
model: string | null;
|
|
13
|
+
startedAt: string;
|
|
14
|
+
endedAt: string | null;
|
|
15
|
+
state: AgentLiveState;
|
|
16
|
+
/** Why it needs you: it has been quiet (the board's Stuck rule) or it asked a question. */
|
|
17
|
+
why: "quiet" | "question" | null;
|
|
18
|
+
/** Since when it has been quiet, when that is why. */
|
|
19
|
+
quietSince: string | null;
|
|
20
|
+
/** Its output is on this computer (another runner's can't be read here). */
|
|
21
|
+
local: boolean;
|
|
22
|
+
/** A repair's build: the tile it nests under. */
|
|
23
|
+
parentRun: number | null;
|
|
24
|
+
/** The task's own page, for a task this console keeps (an outside tracker's task has none here). */
|
|
25
|
+
taskHref: string | null;
|
|
26
|
+
};
|
|
27
|
+
/** The grid's tiles for one viewer: `canSee` is the caller's proof (ceiling and account access) for each run. */
|
|
28
|
+
export declare function agentsLiveOf(store: Store, now: Date, viewer: {
|
|
29
|
+
canSee: (run: AgentsLiveRun) => boolean;
|
|
30
|
+
localRunner: string | undefined;
|
|
31
|
+
}): AgentLiveTile[];
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Agents view's Live grid (docs/plans/agents-view.md §1, §2.5): which agents are running now (every role) and
|
|
3
|
+
* which ended in the last ten minutes, with the facts a tile shows. Store facts only — it verifies nothing, grants
|
|
4
|
+
* nothing, and reads no live file; visibility is proved by the caller's `canSee` for each run.
|
|
5
|
+
*/
|
|
6
|
+
import { attemptKeyOf, agentNowForRun, openAgentQuestions } from "./agent-board.js";
|
|
7
|
+
import { agentsLiveRuns } from "./store.js";
|
|
8
|
+
/** An ended agent stays on the grid this long, then leaves (it remains on the List). */
|
|
9
|
+
export const LIVE_ENDED_KEEP_MS = 10 * 60_000;
|
|
10
|
+
const stateOf = (outcome) => outcome === null ? "working" : outcome === "built" || outcome === "no-change" ? "done" : outcome === "parked" ? "parked" : outcome === "interrupted" ? "stopped" : "failed";
|
|
11
|
+
/** The grid's tiles for one viewer: `canSee` is the caller's proof (ceiling and account access) for each run. */
|
|
12
|
+
export function agentsLiveOf(store, now, viewer) {
|
|
13
|
+
const tiles = [];
|
|
14
|
+
for (const run of agentsLiveRuns(store, new Date(now.getTime() - LIVE_ENDED_KEEP_MS))) {
|
|
15
|
+
if (!viewer.canSee(run))
|
|
16
|
+
continue;
|
|
17
|
+
let state = stateOf(run.outcome);
|
|
18
|
+
let why = null, quietSince = null;
|
|
19
|
+
if (state === "working") {
|
|
20
|
+
try {
|
|
21
|
+
const key = attemptKeyOf(store, run.id);
|
|
22
|
+
const facts = key === null ? null : agentNowForRun(store, run.id, key);
|
|
23
|
+
if (run.role === "builder" && openAgentQuestions(store, run.taskRef).length > 0)
|
|
24
|
+
why = "question";
|
|
25
|
+
else if (facts !== null && facts.stuckAt !== null) {
|
|
26
|
+
why = "quiet";
|
|
27
|
+
quietSince = facts.stuckAt;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch { /* display state: no word yet */ }
|
|
31
|
+
if (why !== null)
|
|
32
|
+
state = "needs-you";
|
|
33
|
+
}
|
|
34
|
+
tiles.push({
|
|
35
|
+
runId: run.id, role: run.role, taskId: run.taskId, title: run.title, project: run.repo, agent: run.provider, model: run.model,
|
|
36
|
+
startedAt: run.startedAt, endedAt: run.finishedAt, state, why, quietSince,
|
|
37
|
+
local: viewer.localRunner !== undefined && run.runner === viewer.localRunner,
|
|
38
|
+
parentRun: run.role === "repair" ? run.parentRun : null,
|
|
39
|
+
taskHref: run.builtIn ? `/t/${encodeURIComponent(run.taskId)}` : null,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return tiles;
|
|
43
|
+
}
|
|
@@ -321,6 +321,46 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
321
321
|
SOFTWARE.
|
|
322
322
|
|
|
323
323
|
|
|
324
|
+
## ReUI — MIT
|
|
325
|
+
|
|
326
|
+
Components in `src/browser/components/ui/` adapt free, MIT-licensed ReUI
|
|
327
|
+
primitives and examples (the Radix base) from:
|
|
328
|
+
https://github.com/keenthemes/reui/tree/067aa0d/registry-reui/bases/radix/reui
|
|
329
|
+
|
|
330
|
+
Source inspected 27 October 2026, revision `067aa0d`. Files copied by hand (the
|
|
331
|
+
shadcn CLI and ReUI's registry CSS variables are not used) and cut to Toolroll's
|
|
332
|
+
tokens:
|
|
333
|
+
|
|
334
|
+
- `badge.tsx` — the light badge and its status-dot example (`c-badge-22`): one
|
|
335
|
+
20px chip size and a decorative dot on the existing tones, plus a "Needs you"
|
|
336
|
+
tone. The style variants, solid fills and focus/invert tones are omitted.
|
|
337
|
+
- `frame.tsx` — the well of panels: one geometry (a raised well, hairline,
|
|
338
|
+
concentric panel corners) with the style variants, spacing scale, stacking
|
|
339
|
+
modes and panel shadows omitted; `FramePanel` can render as its child.
|
|
340
|
+
- `alert.tsx` — cut to a neutral and a destructive tone with an icon column;
|
|
341
|
+
the info, warning, success and invert tones, the title/action slots and the
|
|
342
|
+
style variants are omitted.
|
|
343
|
+
- `timeline.tsx` — cut to a read-only vertical list (item, indicator on a
|
|
344
|
+
hairline rail, content, date); the active-step context, horizontal mode and
|
|
345
|
+
styled indicators are omitted.
|
|
346
|
+
- `stepper.tsx` — its read-only horizontal layout (indicator, separator,
|
|
347
|
+
title, `aria-current="step"`) is followed by the task page's road
|
|
348
|
+
(`src/browser/first-run.tsx` `Journey`); no stepper source is copied, and
|
|
349
|
+
the controlled state, keyboard navigation and triggers are omitted.
|
|
350
|
+
|
|
351
|
+
No ReUI Pro blocks, icons or templates are included.
|
|
352
|
+
|
|
353
|
+
MIT License
|
|
354
|
+
|
|
355
|
+
Copyright (c) 2025 Keenthemes Inc
|
|
356
|
+
|
|
357
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
358
|
+
|
|
359
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
360
|
+
|
|
361
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
362
|
+
|
|
363
|
+
|
|
324
364
|
## Integration logos: Simple Icons (CC0 1.0) and svgl (MIT)
|
|
325
365
|
|
|
326
366
|
The integration logos in `src/brand-icons.ts` are generated by
|