toolroll 0.9.68 → 0.9.69
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 +34 -0
- package/dist/agent-fence.d.ts +58 -0
- package/dist/agent-fence.js +211 -19
- package/dist/agentconfig.d.ts +1 -1
- package/dist/agentconfig.js +6 -2
- package/dist/batch-checks.js +14 -26
- package/dist/builder.d.ts +9 -0
- package/dist/builder.js +27 -1
- package/dist/check-access.d.ts +54 -0
- package/dist/check-access.js +80 -0
- package/dist/contracts/console-api.d.ts +1 -0
- package/dist/contracts/console-api.js +1 -0
- package/dist/invoke.d.ts +39 -2
- package/dist/invoke.js +176 -21
- package/dist/operate.d.ts +4 -0
- package/dist/operate.js +73 -0
- package/dist/project-delete-ui.d.ts +16 -0
- package/dist/project-delete-ui.js +60 -1
- package/dist/result-follow-ups.js +10 -15
- package/dist/scope.d.ts +9 -0
- package/dist/scope.js +12 -5
- package/dist/serve.js +9 -0
- package/dist/server/http.d.ts +2 -0
- package/dist/server/render-results.d.ts +3 -0
- package/dist/server/render-results.js +1 -0
- package/dist/server/render-tasks.js +2 -1
- package/dist/server/route-table.js +1 -0
- package/dist/server/settings.js +37 -0
- package/dist/server/tasks.js +2 -0
- package/dist/store.d.ts +29 -2
- package/dist/store.js +86 -3
- package/dist/surface.js +2 -1
- package/package.json +1 -1
package/dist/store.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { WRITES_DEFAULT } from "./writes-mode.js";
|
|
2
|
-
import {
|
|
2
|
+
import { checkAccessOf, widerThan } from "./check-access.js";
|
|
3
|
+
import { networkFromValue, PROJECT_NETWORK_DEFAULT, projectNetworkFromValue, runNetworkFromValue } from "./egress.js";
|
|
3
4
|
import { isAcpProfile, isAcpProvider } from "./acp-agents.js";
|
|
4
5
|
import { ACP_CONFORMANCE_VERSION, launchFromJson } from "./acp-attest.js";
|
|
5
6
|
import { reclaimDatabase } from "./database-reclaim.js";
|
|
@@ -718,7 +719,9 @@ const REGISTERED_AT_FILING = "again, as it was filed";
|
|
|
718
719
|
// (confinement_setting, one row; off unless an instance operator turns it on).
|
|
719
720
|
// v131 adds, and changes no saved row: where a project's builds may write (project_writes; agent-fence.ts, docs/plans/
|
|
720
721
|
// agent-baseline-hardening.md arc 2) — only their workspace unless an approver chose anywhere.
|
|
721
|
-
|
|
722
|
+
// v132 adds, and changes no saved row: how far a project's checks reach beyond the check fence (project_check_access;
|
|
723
|
+
// check-access.ts, docs/plans/check-access.md) — sandboxed unless an instance operator widened it.
|
|
724
|
+
export const SCHEMA_VERSION = 132;
|
|
722
725
|
/** v118: the tables merged into `credential`, in the order they move (a person's API tokens keep their ids first). */
|
|
723
726
|
export const V118_MERGED_TABLES = Object.freeze({ api_token: "person", coordinator_credential: "coordinator", lead_credential: "lead" });
|
|
724
727
|
/** The foreign keys that named a merged table, exactly as their DDL wrote them; each names `credential(id)` instead. */
|
|
@@ -851,7 +854,7 @@ const V115_DROPPED_REFERENCES = /\s+REFERENCES (?:contestant|contest|tournament_
|
|
|
851
854
|
* unchanged), and adds the nullable column in V125_ADDED_COLUMNS, checked the same way. v126 only adds a new table
|
|
852
855
|
* (task_source) and the columns in V126_ADDED_COLUMNS (null or 0), checked the same way.
|
|
853
856
|
*/
|
|
854
|
-
export const UPDATE_SAFE_MIGRATIONS = Object.freeze([108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131]);
|
|
857
|
+
export const UPDATE_SAFE_MIGRATIONS = Object.freeze([108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132]);
|
|
855
858
|
/** v119: the columns it adds, by table, with the value every row saved before it reads (toolroll-update's HISTORY_RULES). */
|
|
856
859
|
export const V119_ADDED_COLUMNS = Object.freeze({
|
|
857
860
|
task_ref: { planning_generation: { definition: "INTEGER NOT NULL DEFAULT 0", saved: "0" } },
|
|
@@ -988,6 +991,15 @@ CREATE TABLE IF NOT EXISTS project_writes (
|
|
|
988
991
|
updated_by TEXT NOT NULL
|
|
989
992
|
);
|
|
990
993
|
`;
|
|
994
|
+
/** v132: how far a project's checks reach beyond the check fence (check-access.ts CheckAccess). */
|
|
995
|
+
export const PROJECT_CHECK_ACCESS_SCHEMA = `
|
|
996
|
+
CREATE TABLE IF NOT EXISTS project_check_access (
|
|
997
|
+
repo TEXT PRIMARY KEY,
|
|
998
|
+
level TEXT NOT NULL CHECK (level IN ('sandboxed', 'sign-ins', 'unsandboxed')),
|
|
999
|
+
updated_at TEXT NOT NULL,
|
|
1000
|
+
updated_by TEXT NOT NULL
|
|
1001
|
+
);
|
|
1002
|
+
`;
|
|
991
1003
|
/** v129: a finished run's How it ran, derived from its own records on first view (display only: never a verdict). Its
|
|
992
1004
|
* parser version says when to derive again; `steps` says how complete the step detail was, and why not. */
|
|
993
1005
|
export const RUN_EFFICIENCY_SCHEMA = `
|
|
@@ -3923,6 +3935,11 @@ function initializeStore(db, file) {
|
|
|
3923
3935
|
db.exec(CONFINEMENT_SCHEMA);
|
|
3924
3936
|
// v131: where a project's builds may write (a new table only).
|
|
3925
3937
|
db.exec(PROJECT_WRITES_SCHEMA);
|
|
3938
|
+
// v132: how far a project's checks reach beyond the check fence (a new table), and the level each build's checks ran
|
|
3939
|
+
// at (run.sandbox_access; saved rows read NULL: sandboxed).
|
|
3940
|
+
db.exec(PROJECT_CHECK_ACCESS_SCHEMA);
|
|
3941
|
+
addColumn(db, "run", "sandbox_access", "TEXT");
|
|
3942
|
+
addColumn(db, "run", "sandbox_network", "TEXT");
|
|
3926
3943
|
// v123: a flow that runs on its own (flow-unattended.ts): its grants and daily runs (new tables), and which authority
|
|
3927
3944
|
// armed a plan's unchanged-plan approval (plan_authorization.basis, V123_ADDED_COLUMNS: saved rows read the mode).
|
|
3928
3945
|
db.exec(FLOW_UNATTENDED_SCHEMA);
|
|
@@ -6729,6 +6746,19 @@ export class Store {
|
|
|
6729
6746
|
.get(taskId);
|
|
6730
6747
|
if (writtenByLead !== undefined)
|
|
6731
6748
|
return false;
|
|
6749
|
+
// CHECK ACCESS (v132, check-access.ts): a scope whose checks reach beyond the fence (sign-ins, or no sandbox at
|
|
6750
|
+
// all) seals only on a person's password ceremony — never a mode, a grant, a chat confirmation or a carry.
|
|
6751
|
+
const profileText = this.db.prepare("SELECT profile_json FROM task_scope WHERE task_id = ?").get(taskId)?.["profile_json"];
|
|
6752
|
+
let sealedAccess;
|
|
6753
|
+
try {
|
|
6754
|
+
const stored = typeof profileText === "string" ? JSON.parse(profileText) : null;
|
|
6755
|
+
sealedAccess = stored?.profile?.["checkAccess"];
|
|
6756
|
+
}
|
|
6757
|
+
catch {
|
|
6758
|
+
sealedAccess = "unreadable";
|
|
6759
|
+
}
|
|
6760
|
+
if (sealedAccess !== undefined)
|
|
6761
|
+
return false;
|
|
6732
6762
|
}
|
|
6733
6763
|
// SEPARATION OF DUTIES (v102), enforced in the primitive so no road
|
|
6734
6764
|
// around it exists: a project's rules can refuse the requester, and
|
|
@@ -18987,6 +19017,59 @@ export class Store {
|
|
|
18987
19017
|
this.db.prepare("UPDATE lead_queue SET context_json = NULL WHERE message = ?").run(message);
|
|
18988
19018
|
this.db.prepare("DELETE FROM memory_search WHERE kind = 'conversation' AND ref = ?").run(`message:${message}`);
|
|
18989
19019
|
}
|
|
19020
|
+
// ---- check access (v132, check-access.ts). Kept last in the class: the freshness tripwire pins method positions.
|
|
19021
|
+
/** How far a project's checks reach beyond the check fence: its saved level, or sandboxed. */
|
|
19022
|
+
projectCheckAccess(repo) {
|
|
19023
|
+
// A damaged row reads as the narrowest level, never a wider one.
|
|
19024
|
+
return checkAccessOf(this.db.prepare("SELECT level FROM project_check_access WHERE repo = ?").get(repo)?.["level"]);
|
|
19025
|
+
}
|
|
19026
|
+
/** Who last changed a project's check access, and when (null: never changed). */
|
|
19027
|
+
projectCheckAccessChange(repo) {
|
|
19028
|
+
const row = this.db.prepare("SELECT updated_by, updated_at FROM project_check_access WHERE repo = ?").get(repo);
|
|
19029
|
+
return row === undefined ? null : { by: String(row["updated_by"]), at: String(row["updated_at"]) };
|
|
19030
|
+
}
|
|
19031
|
+
/** The level a run's setup and checks ran at (its sealed approval's, or unsandboxed where no OS fence exists). Its
|
|
19032
|
+
* follow-up and batch checks never run wider than this. */
|
|
19033
|
+
stampRunCheckAccess(runId, level, network = null) {
|
|
19034
|
+
this.db.prepare("UPDATE run SET sandbox_access = ?, sandbox_network = ? WHERE id = ?").run(level, network === null ? null : JSON.stringify(network), runId);
|
|
19035
|
+
}
|
|
19036
|
+
/** The network a run's sign-ins checks ran under (null: none sealed; such a run's later checks run sandboxed). */
|
|
19037
|
+
runCheckNetwork(runId) {
|
|
19038
|
+
const text = this.db.prepare("SELECT sandbox_network FROM run WHERE id = ?").get(runId)?.["sandbox_network"];
|
|
19039
|
+
if (typeof text !== "string")
|
|
19040
|
+
return null;
|
|
19041
|
+
try {
|
|
19042
|
+
return networkFromValue(JSON.parse(text));
|
|
19043
|
+
}
|
|
19044
|
+
catch {
|
|
19045
|
+
return null;
|
|
19046
|
+
}
|
|
19047
|
+
}
|
|
19048
|
+
/** The level a run's checks ran at: missing or unreadable reads as sandboxed. */
|
|
19049
|
+
runCheckAccess(runId) {
|
|
19050
|
+
return checkAccessOf(this.db.prepare("SELECT sandbox_access FROM run WHERE id = ?").get(runId)?.["sandbox_access"]);
|
|
19051
|
+
}
|
|
19052
|
+
/** A project's check access changed: saved, and in the ledger as before → after. `may` is what the caller proved:
|
|
19053
|
+
* "widen" only after an instance operator's password typed now; anything else may only narrow. The level is read again
|
|
19054
|
+
* and compared in the same transaction as the write, so a change made while the caller was proving itself can't turn
|
|
19055
|
+
* a narrowing into a widening. Refused: null, nothing written. */
|
|
19056
|
+
setProjectCheckAccess(repo, level, by, now, authority) {
|
|
19057
|
+
return this.transact(() => {
|
|
19058
|
+
const before = this.projectCheckAccess(repo);
|
|
19059
|
+
// Who is acting is proved again here too: an account revoked, demoted or moved off the project while its password
|
|
19060
|
+
// was being checked changes nothing.
|
|
19061
|
+
const account = this.accountOf(by);
|
|
19062
|
+
if (account === null || account.revokedAt !== null || account.role !== "approver" || !this.accountCanAccess(by, repo))
|
|
19063
|
+
return null;
|
|
19064
|
+
if (widerThan(level, before) && (authority.may !== "widen" || !this.isInstanceOperator(by)))
|
|
19065
|
+
return null;
|
|
19066
|
+
this.db.prepare(`INSERT INTO project_check_access (repo, level, updated_at, updated_by) VALUES (?, ?, ?, ?)
|
|
19067
|
+
ON CONFLICT (repo) DO UPDATE SET level = excluded.level, updated_at = excluded.updated_at, updated_by = excluded.updated_by`).run(repo, level, now.toISOString(), by);
|
|
19068
|
+
const after = this.projectCheckAccess(repo);
|
|
19069
|
+
this.recordPolicy(by, repo, "check access", before, after, now);
|
|
19070
|
+
return { before, after };
|
|
19071
|
+
});
|
|
19072
|
+
}
|
|
18990
19073
|
}
|
|
18991
19074
|
/** A stored count or id that must be a whole, non-negative number — a
|
|
18992
19075
|
* NULL, an empty string, a fraction, or text reads as null (v48 authority repair). */
|
package/dist/surface.js
CHANGED
|
@@ -353,6 +353,7 @@ const GUIDE_ROWS = [
|
|
|
353
353
|
operator("runner bind", "replace which repositories a worker may build in"),
|
|
354
354
|
operator("runner capacity", "set how many tasks a worker runs at once; running work carries on, the new number applies at the next claim"),
|
|
355
355
|
operator("project checks", "show or set a project's batch checks (--batch on|off, off by default): results that finish within 10 minutes share one full check on a temporary batch commit; nothing is merged into a real branch"),
|
|
356
|
+
operator("project check-access", "show or set how far a project's checks reach: sandboxed (the default), with model and GitHub sign-ins, or not sandboxed; an instance operator widens it with a password typed now"),
|
|
356
357
|
operator("project concurrency", "set how many of a project's tasks build at once (default 2, never past the worker's capacity); running work carries on"),
|
|
357
358
|
operator("coordinator mint", "mint the MCP filing credential — repo-bound, rate-limited, token shown once"),
|
|
358
359
|
operator("coordinator revoke", "revoke an MCP filing credential"),
|
|
@@ -450,7 +451,7 @@ const REMOTE_STEP_UP = [
|
|
|
450
451
|
// A person's own API tokens: a password step-up on the server's machine, or the console. Never with a token.
|
|
451
452
|
"tokens create", "tokens list", "tokens revoke", "tokens rotate",
|
|
452
453
|
"mode show", "mode set", "mode revoke", "chat-approval on", "review on", "review off", "config set", "config clear",
|
|
453
|
-
"verify set", "verify clear", "verify level", "project checks", "project concurrency", "intake grant", "intake clear", "webhook primary",
|
|
454
|
+
"verify set", "verify clear", "verify level", "project checks", "project check-access", "project concurrency", "intake grant", "intake clear", "webhook primary",
|
|
454
455
|
];
|
|
455
456
|
const REMOTE_YES = [
|
|
456
457
|
"status", "integrations", "ready", "gaps", "grants", "sync", "audit",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolroll",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.69",
|
|
4
4
|
"description": "A control plane for unattended coding agents — queue tasks, walk away, come back to pull requests. Agents build in leased worktrees, never touch your default branch, and interrupt you only for decisions that need a human.",
|
|
5
5
|
"directories": {
|
|
6
6
|
"doc": "docs"
|