toolroll 0.9.76 → 0.9.78
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 +273 -0
- package/README.md +11 -4
- package/THIRD_PARTY_NOTICES.md +5 -5
- package/dist/agent-fence.js +46 -10
- package/dist/assignment.js +3 -0
- package/dist/backup-ui.d.ts +9 -1
- package/dist/backup-ui.js +28 -3
- package/dist/backup.js +4 -3
- package/dist/browser/THIRD_PARTY_NOTICES.txt +5 -5
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +31 -31
- package/dist/browser-workspace.d.ts +2 -0
- package/dist/builder.d.ts +18 -3
- package/dist/builder.js +152 -47
- package/dist/chat-decide.js +4 -0
- package/dist/check-failure.d.ts +92 -0
- package/dist/check-failure.js +295 -0
- package/dist/check-progress.d.ts +28 -1
- package/dist/check-progress.js +105 -6
- package/dist/check-report.d.ts +67 -6
- package/dist/check-report.js +254 -9
- package/dist/checkout-cleanup.js +2 -2
- package/dist/claim.d.ts +9 -0
- package/dist/claim.js +9 -1
- package/dist/cli.js +8 -5
- package/dist/coding-handoff.d.ts +1 -0
- package/dist/coding-handoff.js +20 -11
- package/dist/commit-extract.d.ts +6 -0
- package/dist/commit-extract.js +63 -8
- package/dist/console-chrome.d.ts +41 -0
- package/dist/console-chrome.js +166 -0
- package/dist/contracts/console-api.d.ts +1 -1
- package/dist/contracts/console-api.js +1 -1
- package/dist/contracts/release-check.d.ts +12 -0
- package/dist/contracts/release-check.js +17 -0
- package/dist/contracts/release-terms.d.ts +41 -0
- package/dist/contracts/release-terms.js +53 -0
- package/dist/contracts/verification-receipt.d.ts +20 -2
- package/dist/contracts/verification-receipt.js +15 -3
- package/dist/contracts/verification-view.d.ts +8 -0
- package/dist/controller-service.js +4 -1
- package/dist/controller-supervisor.d.ts +8 -0
- package/dist/controller-supervisor.js +33 -1
- package/dist/demo.js +3 -2
- package/dist/deploy-gate.js +5 -0
- package/dist/deploy-rescue.d.ts +41 -0
- package/dist/deploy-rescue.js +94 -0
- package/dist/desktop-update.d.ts +1 -1
- package/dist/dispose.d.ts +3 -0
- package/dist/dispose.js +9 -5
- package/dist/doctor.d.ts +7 -0
- package/dist/doctor.js +10 -2
- package/dist/envelope.js +2 -1
- package/dist/evidence.d.ts +9 -0
- package/dist/evidence.js +19 -4
- package/dist/exec.d.ts +9 -0
- package/dist/exec.js +22 -2
- package/dist/git-floor.d.ts +16 -0
- package/dist/git-floor.js +36 -0
- package/dist/grant.js +5 -1
- package/dist/guides.js +22 -11
- package/dist/latency-timeline.d.ts +46 -0
- package/dist/latency-timeline.js +120 -0
- package/dist/live-backup.d.ts +39 -0
- package/dist/live-backup.js +271 -0
- package/dist/operate.d.ts +13 -0
- package/dist/operate.js +969 -328
- package/dist/planner.js +2 -2
- package/dist/prepared-evidence.d.ts +2 -1
- package/dist/prepared-evidence.js +8 -3
- package/dist/project-knowledge.js +6 -2
- package/dist/project-learning.js +2 -2
- package/dist/proof.js +6 -0
- package/dist/read-executor-worker.js +8 -4
- package/dist/read-executor.d.ts +18 -1
- package/dist/read-executor.js +8 -0
- package/dist/release-adapters.d.ts +80 -2
- package/dist/release-adapters.js +441 -16
- package/dist/release-authority.js +6 -2
- package/dist/release-auto.d.ts +38 -7
- package/dist/release-auto.js +131 -15
- package/dist/release-card.d.ts +15 -3
- package/dist/release-card.js +25 -9
- package/dist/release-carry.d.ts +21 -1
- package/dist/release-carry.js +22 -0
- package/dist/release-ci-hold.d.ts +41 -0
- package/dist/release-ci-hold.js +53 -0
- package/dist/release-cli.d.ts +3 -0
- package/dist/release-cli.js +95 -17
- package/dist/release-coverage.js +5 -2
- package/dist/release-gate.d.ts +5 -0
- package/dist/release-gate.js +1 -1
- package/dist/release-recheck.d.ts +1 -1
- package/dist/release-recheck.js +8 -2
- package/dist/release.d.ts +144 -14
- package/dist/release.js +421 -81
- package/dist/review-context.js +8 -6
- package/dist/runner-process.d.ts +67 -0
- package/dist/runner-process.js +219 -0
- package/dist/runner.d.ts +48 -6
- package/dist/runner.js +73 -9
- package/dist/sandbox-runtime.d.ts +8 -0
- package/dist/sandbox-runtime.js +14 -2
- package/dist/scope.d.ts +19 -1
- package/dist/scope.js +26 -3
- package/dist/scout.js +2 -2
- package/dist/secret-mint.d.ts +1 -0
- package/dist/secret-mint.js +14 -0
- package/dist/serve.js +112 -51
- package/dist/server/chat.js +5 -5
- package/dist/server/http.d.ts +2 -0
- package/dist/server/pages.js +7 -3
- package/dist/server/remote-hooks.d.ts +1 -1
- package/dist/server/remote-hooks.js +5 -3
- package/dist/server/render-results.d.ts +2 -0
- package/dist/server/render-results.js +1 -0
- package/dist/server/render-tasks.d.ts +2 -0
- package/dist/server/render-tasks.js +2 -1
- package/dist/server/request-context.d.ts +8 -0
- package/dist/server/runtime.d.ts +2 -0
- package/dist/server/settings.js +22 -2
- package/dist/server/task-views.js +7 -2
- package/dist/server/tasks.js +4 -1
- package/dist/setup-guide.d.ts +2 -2
- package/dist/storage-sweep.js +1 -1
- package/dist/storage.d.ts +5 -0
- package/dist/storage.js +28 -1
- package/dist/store.d.ts +135 -2
- package/dist/store.js +275 -13
- package/dist/surface.js +3 -1
- package/dist/task-base.d.ts +4 -0
- package/dist/task-base.js +22 -2
- package/dist/teardown-git.d.ts +52 -3
- package/dist/teardown-git.js +145 -33
- package/dist/test-infra.js +1 -1
- package/dist/toolroll-update.js +2 -2
- package/dist/up-processes.d.ts +137 -0
- package/dist/up-processes.js +165 -0
- package/dist/up-role-child.d.ts +1 -0
- package/dist/up-role-child.js +81 -0
- package/dist/verification-evidence.d.ts +1 -0
- package/dist/verification-evidence.js +13 -1
- package/dist/workspace-revision.js +3 -1
- package/package.json +2 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** Task pages: approval, agents, plans, controls, decisions and the task body. */
|
|
2
2
|
import { networkWords } from "../egress.js";
|
|
3
3
|
import { releaseAsksAgainWords, releaseTermsSentence } from "../scope.js";
|
|
4
|
+
import { RELEASE_CI_RUNNING_WORDS } from "../release-ci-hold.js";
|
|
4
5
|
import { writesWords } from "../writes-mode.js";
|
|
5
6
|
import { checkAccessWords } from "../check-access.js";
|
|
6
7
|
import { sandboxTermsWords } from "../project-sandbox.js";
|
|
@@ -1501,7 +1502,7 @@ export function taskBodyParts(data) {
|
|
|
1501
1502
|
scope.touches.length === 0 ? "" : html `<p class="scope-paths"><strong>Touches</strong> ${scope.touches.join(", ")}</p>`,
|
|
1502
1503
|
html `<p><strong>Quality</strong> ${qualityModeTitle(scope.qualityMode ?? "default")}</p>`,
|
|
1503
1504
|
acceptanceCeremonyHtml(scope.acceptance),
|
|
1504
|
-
scope.releaseTerms == null ? "" : html `<p><strong>Release</strong></p><p class="recap">${releaseTermsSentence(scope.releaseTerms)}</p>${scope.releaseTerms.policy.carry ? html `<details><summary>What asks again</summary><ul>${joinHtml(releaseAsksAgainWords(scope.releaseTerms).map(line => html `<li>${line}</li>`), "")}</ul></details>` : ""}`,
|
|
1505
|
+
scope.releaseTerms == null ? "" : html `<p><strong>Release</strong></p><p class="recap">${releaseTermsSentence(scope.releaseTerms)}</p>${data.releaseCiRunning === true ? html `<p class="meta">${RELEASE_CI_RUNNING_WORDS}</p>` : ""}${scope.releaseTerms.policy.carry ? html `<details><summary>What asks again</summary><ul>${joinHtml(releaseAsksAgainWords(scope.releaseTerms).map(line => html `<li>${line}</li>`), "")}</ul></details>` : ""}`,
|
|
1505
1506
|
approval.approved && data.autoRelease != null
|
|
1506
1507
|
? html `<p class="meta scope-seal">${autoReleaseWords(data.autoRelease)} · <span class="seal">signs ${shortDigest(scope.digest)}</span></p>`
|
|
1507
1508
|
: approval.approved && data.release?.kind === "carried"
|
|
@@ -18,14 +18,22 @@ export type RequestFacts = {
|
|
|
18
18
|
returnTo: string;
|
|
19
19
|
actor?: string;
|
|
20
20
|
createdTask?: string;
|
|
21
|
+
/** This request's latency timeline (latency-timeline.ts): kept when the answer was slow. */
|
|
22
|
+
timeline?: import("../latency-timeline.js").Timeline;
|
|
21
23
|
browser?: boolean;
|
|
22
24
|
workspaceRead?: boolean;
|
|
25
|
+
/** Receipt reconciliation, task and result reads (`request`, `task`, `result`): work is read afresh, never cached. */
|
|
26
|
+
freshWork?: boolean;
|
|
27
|
+
/** A workspace re-read's If-None-Match: the content validator it holds from its last read. */
|
|
28
|
+
workspaceIfNoneMatch?: string;
|
|
23
29
|
workspaceRequest?: string | null;
|
|
24
30
|
workCounts?: ReturnType<typeof workCountsByProject>;
|
|
25
31
|
workCrew?: {
|
|
26
32
|
project: string | null;
|
|
27
33
|
page: WorkIndexPage;
|
|
28
34
|
};
|
|
35
|
+
/** The chrome this request already read (console-chrome.ts keys): one snapshot per request, however often it asks. */
|
|
36
|
+
chromeReads?: Map<string, unknown>;
|
|
29
37
|
lens?: string | null;
|
|
30
38
|
workspaceValidator?: {
|
|
31
39
|
key: string;
|
package/dist/server/runtime.d.ts
CHANGED
|
@@ -153,6 +153,8 @@ export interface ServerRuntime {
|
|
|
153
153
|
saturated: boolean;
|
|
154
154
|
};
|
|
155
155
|
liveRefreshSeconds: () => number;
|
|
156
|
+
/** One work index page for an already-admitted reader, read at most once per workspace revision (console-chrome.ts). */
|
|
157
|
+
sharedIndexPage: (access: import("../work-summary.js").WorkSummaryAccess, options: import("../work-index.js").WorkIndexOptions) => import("../work-index.js").WorkIndexPage;
|
|
156
158
|
chatKeyFor: (provider: DirectChatProviderId) => {
|
|
157
159
|
key: string;
|
|
158
160
|
source: "environment" | "stored";
|
package/dist/server/settings.js
CHANGED
|
@@ -117,7 +117,9 @@ import { sweepSchedule } from "../storage-sweep-schedule.js";
|
|
|
117
117
|
import { storageHtml } from "../storage-ui.js";
|
|
118
118
|
import { landedSummary } from "../landed-cleanup.js";
|
|
119
119
|
import { bytesWords, parseCleanup } from "../storage.js";
|
|
120
|
-
import { isDigestTime, RESULT_SCREENSHOTS } from "../store.js";
|
|
120
|
+
import { isDigestTime, liveBackupState, RESULT_SCREENSHOTS, setLiveBackupSettings } from "../store.js";
|
|
121
|
+
import { defaultLiveFolder, liveFolderOf, liveFolderProblem } from "../live-backup.js";
|
|
122
|
+
import { LIVE_EVERY } from "../backup-ui.js";
|
|
121
123
|
import { checkTeamsCredentials, clearTeamsCredentials, loadTeamsCredentials, saveTeamsCredentials, TeamsError } from "../teams-api.js";
|
|
122
124
|
import { teamsSettingsHtml } from "../teams-settings.js";
|
|
123
125
|
import { telegramSettingsHtml } from "../telegram-settings.js";
|
|
@@ -497,7 +499,9 @@ ${connectionHtml(asked, connection, task)}${command === null ? "" : html `<p>On
|
|
|
497
499
|
if (databaseFile === null)
|
|
498
500
|
return refuse(response, who, 403, "An instance operator looks after backups.", "/settings");
|
|
499
501
|
const settings = store.backupSettings();
|
|
500
|
-
const
|
|
502
|
+
const live = liveBackupState(store);
|
|
503
|
+
const view = { settings, folder: backupFolderOf(settings, databaseFile), defaultFolder: defaultBackupFolder(databaseFile), runs: store.backupRuns(20), csrf: who.session.csrf, now,
|
|
504
|
+
live: { state: live, folder: liveFolderOf(live.settings, databaseFile), defaultFolder: defaultLiveFolder(databaseFile) } };
|
|
501
505
|
return sendScreen(response, 200, screen("Backups", html `<p><a href="/settings">Settings</a></p><h1>Backups</h1>${(backupHtml(view, { said: url.searchParams.get("said"), problem: url.searchParams.get("problem") }))}`, { chrome: chromeFor(project, "settings") }));
|
|
502
506
|
}
|
|
503
507
|
// v105: Settings → Data, where an instance operator downloads everything. The download is a POST with the password.
|
|
@@ -1325,8 +1329,24 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1325
1329
|
if (problem !== null)
|
|
1326
1330
|
return back("problem", problem);
|
|
1327
1331
|
}
|
|
1332
|
+
// The continuous copy (live-backup.ts): how often, and a folder that is a plain, private folder of this user's
|
|
1333
|
+
// outside every project and checkout. A form without these fields leaves it as it is.
|
|
1334
|
+
const liveBefore = liveBackupState(store).settings;
|
|
1335
|
+
const liveEvery = body.get("live_every") ?? null;
|
|
1336
|
+
const liveMinutes = Number(liveEvery);
|
|
1337
|
+
if (liveEvery !== null && liveEvery !== "off" && !LIVE_EVERY.includes(liveMinutes))
|
|
1338
|
+
return back("problem", "Choose how often to copy continuously.");
|
|
1339
|
+
const liveTyped = body.get("live_folder") === null ? liveBefore.folder ?? "" : (body.get("live_folder") ?? "").trim();
|
|
1340
|
+
const liveFolder = liveTyped === "" || liveTyped === defaultLiveFolder(databaseFile) ? null : liveTyped;
|
|
1341
|
+
if (liveFolder !== null) {
|
|
1342
|
+
const problem = liveFolderProblem(liveFolder, [...new Set([...managedRepos(), ...store.knownRepos(), ...(options.poolRoot === undefined ? [] : [options.poolRoot])])]);
|
|
1343
|
+
if (problem !== null)
|
|
1344
|
+
return back("problem", problem);
|
|
1345
|
+
}
|
|
1328
1346
|
const before = store.backupSettings();
|
|
1329
1347
|
store.setBackupSettings({ enabled: every !== "off", everyHours: every === "off" ? before.everyHours : hours, keep, folder }, who.name, now);
|
|
1348
|
+
setLiveBackupSettings(store, { enabled: liveEvery === null ? liveBefore.enabled : liveEvery !== "off", everyMinutes: liveEvery === null || liveEvery === "off" ? liveBefore.everyMinutes : liveMinutes,
|
|
1349
|
+
keep: liveBefore.keep, folder: liveFolder }, who.name, now);
|
|
1330
1350
|
return back("said", every === "off" ? "Saved. Scheduled backups are off." : "Saved.");
|
|
1331
1351
|
}
|
|
1332
1352
|
// v105: download everything as a .zip. An instance operator, with a step-up; the ledger records it.
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { releaseViewOf } from "../release-view.js";
|
|
2
|
+
import { releaseCiRunningOf } from "../release-ci-hold.js";
|
|
2
3
|
import { releaseObligationsOf } from "../release-recheck.js";
|
|
3
4
|
import { approvalProofOf, freshlyApprovable, freshnessFor, withFreshness } from "../scope.js";
|
|
4
5
|
import { liveAttemptOf, steerControlsFor } from "../steer-controls.js";
|
|
5
6
|
import { originOf } from "../task-source.js";
|
|
6
|
-
import { taskBaseOf } from "../task-base.js";
|
|
7
|
+
import { recentGitReads, taskBaseOf } from "../task-base.js";
|
|
7
8
|
import { assignmentPresentationOf, historicalAssessmentReason } from '../assignment-presentation.js';
|
|
8
9
|
import { html, joinHtml } from "../html.js";
|
|
9
10
|
import { assignmentStatusOf, assignmentWithEvidence } from '../assignment-ui.js';
|
|
@@ -53,6 +54,9 @@ import {} from "../store.js";
|
|
|
53
54
|
import { taskControlOf } from "../task-control.js";
|
|
54
55
|
import { lastErrorLineOf, missedRequirementOf, workStatusOf } from "../workspace-ui.js";
|
|
55
56
|
export function createTaskViews(runtime) {
|
|
57
|
+
// The task page's base line reads Git through a frozen view of the project (~0.2-0.5 s on a large repository) and
|
|
58
|
+
// the page renders it twice per view and again on every live refresh: its answers are kept for a moment.
|
|
59
|
+
const baseGit = recentGitReads();
|
|
56
60
|
/** The first task's way to Ready shows until the first Ready result, and stays on the installation's first task. */
|
|
57
61
|
function guided(rootId, now) {
|
|
58
62
|
if (runtime.store.isDemo())
|
|
@@ -491,12 +495,13 @@ export function createTaskViews(runtime) {
|
|
|
491
495
|
return { rows, decisions: decisionsFor(runtime.store, rows), now };
|
|
492
496
|
})(),
|
|
493
497
|
steering: ref === null ? [] : runtime.store.listSteerNotes(ref.id),
|
|
494
|
-
base: taskBaseOf(runtime.store, taskId),
|
|
498
|
+
base: taskBaseOf(runtime.store, taskId, baseGit),
|
|
495
499
|
steeringLive: ref !== null && runtime.store.steeringLiveBuild(ref.id, now) !== null,
|
|
496
500
|
steeringRun: ref === null ? null : runtime.store.steeringLiveBuild(ref.id, now),
|
|
497
501
|
release: ref === null ? null : releaseViewOf(runtime.store, ref.externalId, now, authority => releaseObligationsOf(runtime.store, runtime.evidenceRoot, authority)),
|
|
498
502
|
canStopRelease: who.role === "approver" && who.via === "cookie",
|
|
499
503
|
autoRelease: ref === null ? null : autoReleaseOf(runtime.store.getScope(ref.externalId)),
|
|
504
|
+
releaseCiRunning: ref !== null && releaseCiRunningOf(runtime.store, ref.externalId) !== null,
|
|
500
505
|
approvalProof: (() => {
|
|
501
506
|
// The proof of the approval shown: its approver, its exact sealed digest (approvedDigest === digest when approved).
|
|
502
507
|
const scope = ref === null ? null : runtime.store.getScope(ref.externalId);
|
package/dist/server/tasks.js
CHANGED
|
@@ -66,6 +66,7 @@ import { boardTickScript, oneLineOf, QUEUE_VIEW, refuse, regionScript, screen, t
|
|
|
66
66
|
import { chatReturnWithSaid, matchTaskPath, redirect, respond, reviewHref, safeChatReturn, safeReturn, SAFETY, taskChatHref, taskHref } from "./http.js";
|
|
67
67
|
import { boardBody, boardWorkers, donePage, inboxFingerprints, inboxPage, newTaskPage, nextPage, parseInboxTab, QUEUE_FRONT, queueBody, queueScript, TASK_STATES, workPage } from "./render-pages.js";
|
|
68
68
|
import { runEfficiencyOf } from "../run-efficiency.js";
|
|
69
|
+
import { timingFor } from "../latency-timeline.js";
|
|
69
70
|
import { ACCEPT_ANYWAY_NEEDS_REASON, editorFileHref, proofBundleView, rankReviewQueue, RESULT_REFUSALS, REVIEW_QUEUE_CAP, reviewCockpitDetailParts, reviewCockpitPage, reviewPriorityOf, runFactsFragment, runOutcomeBadge, runPage, RUNS_PAGE, runsPage, withRefusal } from "./render-results.js";
|
|
70
71
|
import { approvalFormDigest, approveRefusalWords, checkProgressHtml, consentDoorOf, decisionPage, nonceHashOf, PermissionsWouldChange, requirementsFromEditor, resumeDigestOf, taskOf } from "./render-tasks.js";
|
|
71
72
|
import {} from "./session.js";
|
|
@@ -2837,7 +2838,7 @@ export function createTasksHandlers(runtime) {
|
|
|
2837
2838
|
...(store.isDemo() ? { demo: true } : {}),
|
|
2838
2839
|
};
|
|
2839
2840
|
if (run === null) {
|
|
2840
|
-
return { ...base, run: null, detail: null, reviewRetry: null, review: null, notes: [] };
|
|
2841
|
+
return { ...base, run: null, detail: null, reviewRetry: null, review: null, notes: [], timing: timingFor(store, null, row.taskId) };
|
|
2841
2842
|
}
|
|
2842
2843
|
const detail = resultDetailOf(run, who, now);
|
|
2843
2844
|
return {
|
|
@@ -2866,6 +2867,8 @@ export function createTasksHandlers(runtime) {
|
|
|
2866
2867
|
} })(),
|
|
2867
2868
|
// What its network was and what its proxy saw (egress.ts), display only.
|
|
2868
2869
|
network: store.runNetwork(run.id),
|
|
2870
|
+
// Where the time went: its release check's parts and the task's deploys (latency-timeline.ts), display only.
|
|
2871
|
+
timing: timingFor(store, run.id, row.taskId),
|
|
2869
2872
|
// How far its checks reached (check-access.ts), display only.
|
|
2870
2873
|
checkAccess: store.runCheckAccess(run.id),
|
|
2871
2874
|
reviewRetry: store.reviewRetryStateOf(run.id),
|
package/dist/setup-guide.d.ts
CHANGED
|
@@ -15,10 +15,10 @@ export declare function detectPreparation(repo: string): {
|
|
|
15
15
|
} | null;
|
|
16
16
|
/** Browser installation may write only this known file inside its project. */
|
|
17
17
|
export declare function previewProjectInstructions(repo: string): {
|
|
18
|
-
content?: never;
|
|
19
18
|
ok: false;
|
|
20
19
|
message: string;
|
|
21
20
|
plan?: never;
|
|
21
|
+
content?: never;
|
|
22
22
|
fingerprint?: never;
|
|
23
23
|
installed?: never;
|
|
24
24
|
} | {
|
|
@@ -30,10 +30,10 @@ export declare function previewProjectInstructions(repo: string): {
|
|
|
30
30
|
installed: boolean;
|
|
31
31
|
};
|
|
32
32
|
export declare function addProjectInstructions(repo: string, fingerprint: string): {
|
|
33
|
-
content?: never;
|
|
34
33
|
ok: false;
|
|
35
34
|
message: string;
|
|
36
35
|
plan?: never;
|
|
36
|
+
content?: never;
|
|
37
37
|
fingerprint?: never;
|
|
38
38
|
installed?: never;
|
|
39
39
|
} | import("./skills.js").InstallResult;
|
package/dist/storage-sweep.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Nothing live work uses goes (`liveWork`): while a run is open, a lease current or a checkout in use, each removal
|
|
9
9
|
* and each signal first asks again whether that work uses the folder, and when its processes can't be read nothing
|
|
10
10
|
* goes. Every scan and removal runs in a separate child: synchronous disk work must never delay the worker's lease
|
|
11
|
-
* heartbeats (
|
|
11
|
+
* heartbeats (a lease lasts a minute). On Oct 12 the sweep starved those heartbeats and three builds ended interrupted half a second
|
|
12
12
|
* after it finished. Once a run has ended, the next sweep removes what
|
|
13
13
|
* it no longer uses.
|
|
14
14
|
*
|
package/dist/storage.d.ts
CHANGED
|
@@ -36,3 +36,8 @@ export declare function storageReport(store: Store, databaseFile: string, checko
|
|
|
36
36
|
lines: StorageLine[];
|
|
37
37
|
};
|
|
38
38
|
export declare function bytesWords(bytes: number): string;
|
|
39
|
+
/** diskBytes without holding the event loop: the same one `du` per 200 paths and the same bounds, awaited. A console
|
|
40
|
+
* measuring its checkouts (tens of GB) used to stall every page for as long as `du` took. */
|
|
41
|
+
export declare function diskBytesAsync(paths: readonly string[], run?: typeof duOnce): Promise<Map<string, number>>;
|
|
42
|
+
/** `du -sk` over a batch, its output whatever it managed (a slow disk or a failure answers what it has, or nothing). */
|
|
43
|
+
declare function duOnce(batch: readonly string[]): Promise<string>;
|
package/dist/storage.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* what slips through: stale test temp folders, leftover processes, extra
|
|
8
8
|
* staged runtimes and dependency installs unused for a week.
|
|
9
9
|
*/
|
|
10
|
-
import { spawnSync } from "node:child_process";
|
|
10
|
+
import { execFile, spawnSync } from "node:child_process";
|
|
11
11
|
import { lstatSync, readdirSync } from "node:fs";
|
|
12
12
|
import { basename, dirname, join } from "node:path";
|
|
13
13
|
import { DEPS_FOLDER, sharedCopies } from "./shared-deps.js";
|
|
@@ -190,3 +190,30 @@ export function bytesWords(bytes) {
|
|
|
190
190
|
return `${Math.round(bytes / 1024 ** 2)} MB`;
|
|
191
191
|
return `${Math.round(bytes / 1024)} KB`;
|
|
192
192
|
}
|
|
193
|
+
/** diskBytes without holding the event loop: the same one `du` per 200 paths and the same bounds, awaited. A console
|
|
194
|
+
* measuring its checkouts (tens of GB) used to stall every page for as long as `du` took. */
|
|
195
|
+
export async function diskBytesAsync(paths, run = duOnce) {
|
|
196
|
+
const sizes = new Map();
|
|
197
|
+
if (paths.length === 0)
|
|
198
|
+
return sizes;
|
|
199
|
+
if (process.platform !== "win32") {
|
|
200
|
+
for (let at = 0; at < paths.length; at += 200) {
|
|
201
|
+
const batch = paths.slice(at, at + 200);
|
|
202
|
+
for (const line of (await run(batch)).split("\n")) {
|
|
203
|
+
const match = /^(\d+)\t(.+)$/.exec(line);
|
|
204
|
+
if (match !== null && batch.includes(match[2]))
|
|
205
|
+
sizes.set(match[2], Number(match[1]) * 1024);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
for (const path of paths)
|
|
210
|
+
if (!sizes.has(path))
|
|
211
|
+
sizes.set(path, treeBytes(path));
|
|
212
|
+
return sizes;
|
|
213
|
+
}
|
|
214
|
+
/** `du -sk` over a batch, its output whatever it managed (a slow disk or a failure answers what it has, or nothing). */
|
|
215
|
+
function duOnce(batch) {
|
|
216
|
+
return new Promise(resolve => {
|
|
217
|
+
execFile("du", ["-sk", ...batch], { encoding: "utf8", maxBuffer: 16 * 1024 * 1024, timeout: 60_000 }, (_error, stdout) => resolve(typeof stdout === "string" ? stdout : ""));
|
|
218
|
+
});
|
|
219
|
+
}
|
package/dist/store.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ import type { Scope } from "./scope.js";
|
|
|
26
26
|
import type { QualityMode } from "./quality.js";
|
|
27
27
|
import type { ProgressSnapshot } from "./plan.js";
|
|
28
28
|
import { type CheckProgressSnapshot } from "./check-progress.js";
|
|
29
|
-
import { type ReleaseTerms } from "./contracts/release-terms.js";
|
|
29
|
+
import { type ReleaseCheckPurpose, type ReleaseTerms } from "./contracts/release-terms.js";
|
|
30
30
|
import { type ProjectAccess } from "./project-access.js";
|
|
31
31
|
import { type LedgerEntry } from "./action-ledger.js";
|
|
32
32
|
import type { LimitReading, LimitWindow } from "./provider-limits.js";
|
|
@@ -106,6 +106,10 @@ export declare const LIVE_STEER_SCHEMA = "\nCREATE TABLE IF NOT EXISTS run_pause
|
|
|
106
106
|
/** v136: a run's own "Show commands in the live view" answer, captured once before its live file opens (live-detail.ts).
|
|
107
107
|
* Absent, unreadable or anything but 1 means off. */
|
|
108
108
|
export declare const RUN_LIVE_DETAIL_SCHEMA = "\nCREATE TABLE IF NOT EXISTS run_live_detail (\n run INTEGER PRIMARY KEY REFERENCES run(id) ON DELETE CASCADE,\n commands INTEGER NOT NULL CHECK (commands IN (0,1)),\n captured_at TEXT NOT NULL\n);\n-- The Agents view's Live grid (docs/plans/agents-view.md \u00A72.5): every role's running runs and its recently ended ones.\n-- (id > 0 keeps them to the grid's own query, which names it: no other query's plan is offered them.)\nCREATE INDEX IF NOT EXISTS run_live_open ON run (id) WHERE outcome IS NULL AND id > 0;\nCREATE INDEX IF NOT EXISTS run_live_recent ON run (finished_at) WHERE outcome IS NOT NULL AND id > 0;\n";
|
|
109
|
+
/** v137 (docs/plans/actors-lessons.md): explicit lifecycle, latency timelines and the continuous database copy. New
|
|
110
|
+
* tables only. A lifecycle row's `holder` binds it to one registration (a worker's registered_at) or one console
|
|
111
|
+
* process; a row whose holder no longer matches says nothing. Timelines are bounded and pruned as they are written. */
|
|
112
|
+
export declare const V137_SCHEMA = "\nCREATE TABLE IF NOT EXISTS service_lifecycle (\n component TEXT PRIMARY KEY,\n state TEXT NOT NULL CHECK (state IN ('started','draining','stopped')),\n holder TEXT NOT NULL,\n pid INTEGER,\n changed_at TEXT NOT NULL,\n beat_at TEXT NOT NULL,\n drain_until TEXT\n);\nCREATE TABLE IF NOT EXISTS latency_timeline (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n kind TEXT NOT NULL CHECK (kind IN ('console','release-check','deploy')),\n subject TEXT NOT NULL,\n task_id TEXT,\n run_id INTEGER,\n started_at TEXT NOT NULL,\n total_ms INTEGER NOT NULL,\n milestones_json TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS latency_timeline_by_kind ON latency_timeline (kind, id);\nCREATE INDEX IF NOT EXISTS latency_timeline_by_run ON latency_timeline (run_id) WHERE run_id IS NOT NULL;\nCREATE INDEX IF NOT EXISTS latency_timeline_by_task ON latency_timeline (task_id) WHERE task_id IS NOT NULL;\nCREATE TABLE IF NOT EXISTS live_backup (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n enabled INTEGER NOT NULL DEFAULT 1,\n every_minutes INTEGER NOT NULL DEFAULT 5,\n keep INTEGER NOT NULL DEFAULT 24,\n folder TEXT,\n holder TEXT,\n held_until TEXT,\n latest_file TEXT,\n latest_sha256 TEXT,\n latest_at TEXT,\n latest_schema INTEGER,\n latest_bytes INTEGER,\n tried_at TEXT,\n problem TEXT\n);\n";
|
|
109
113
|
/** A project's "Show commands in the live view" (live-detail.ts): absent means off. */
|
|
110
114
|
export declare const LIVE_DETAIL_SCHEMA = "\nCREATE TABLE IF NOT EXISTS live_detail_setting (\n repo TEXT PRIMARY KEY,\n commands INTEGER NOT NULL CHECK (commands IN (0,1)),\n updated_by TEXT NOT NULL,\n updated_at TEXT NOT NULL\n);\n-- Every run a steering note reached, kept as it happens (live-notes.ts): a\n-- note's own attached/mailed fields move to the next attempt, this does not.\nCREATE TABLE IF NOT EXISTS steer_note_run (\n note INTEGER NOT NULL REFERENCES task_steer(id) ON DELETE CASCADE,\n run INTEGER NOT NULL REFERENCES run(id) ON DELETE CASCADE,\n how TEXT NOT NULL CHECK (how IN ('attached','mailed','read')),\n at TEXT NOT NULL,\n PRIMARY KEY (note, run, how)\n);\nCREATE INDEX IF NOT EXISTS steer_note_run_by_run ON steer_note_run (run, note);\n";
|
|
111
115
|
export declare const CHAT_APPROVAL_SCHEMA = "\nCREATE TABLE IF NOT EXISTS chat_approval_setting (\n approver TEXT NOT NULL,\n scope TEXT NOT NULL,\n enabled INTEGER NOT NULL CHECK (enabled IN (0,1)),\n full_access INTEGER NOT NULL DEFAULT 0 CHECK (full_access IN (0,1)),\n attempt_cap_microusd INTEGER CHECK (attempt_cap_microusd IS NULL OR attempt_cap_microusd >= 0),\n generation INTEGER NOT NULL,\n digest TEXT NOT NULL,\n updated_by TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n PRIMARY KEY (approver, scope)\n);\n";
|
|
@@ -186,7 +190,7 @@ export type SealedLedgerEntry = LedgerEntry & {
|
|
|
186
190
|
hash: string;
|
|
187
191
|
} | null;
|
|
188
192
|
};
|
|
189
|
-
export declare const SCHEMA_VERSION =
|
|
193
|
+
export declare const SCHEMA_VERSION = 138;
|
|
190
194
|
/** v118: the tables merged into `credential`, in the order they move (a person's API tokens keep their ids first). */
|
|
191
195
|
export declare const V118_MERGED_TABLES: Readonly<Record<string, CredentialKind>>;
|
|
192
196
|
/**
|
|
@@ -331,6 +335,12 @@ export declare const V135_ADDED_COLUMNS: Readonly<Record<string, Readonly<Record
|
|
|
331
335
|
definition: string;
|
|
332
336
|
saved: string;
|
|
333
337
|
}>>>>;
|
|
338
|
+
/** v138 (docs/plans/pipeline-speed.md, P0): a scope's release check purpose, part of its digest when present. Saved
|
|
339
|
+
* rows read NULL. */
|
|
340
|
+
export declare const V138_ADDED_COLUMNS: Readonly<Record<string, Readonly<Record<string, {
|
|
341
|
+
definition: string;
|
|
342
|
+
saved: string;
|
|
343
|
+
}>>>>;
|
|
334
344
|
/**
|
|
335
345
|
* v135: one approval per release (docs/plans/release-once.md). `release_authority` is the single source of truth for a
|
|
336
346
|
* release a person approved: one row per generation, changed only by compare-and-swap on `rev`, with a lease whose
|
|
@@ -2339,6 +2349,8 @@ export declare class Store {
|
|
|
2339
2349
|
proposedVia?: ScopeAuthor | null;
|
|
2340
2350
|
/** v135: release terms (release-once.md); undefined keeps the ones the scope carries, null removes them. */
|
|
2341
2351
|
releaseTerms?: ReleaseTerms | null;
|
|
2352
|
+
/** v138: a release check's purpose; undefined keeps the scope's, null removes it. Never beside terms. */
|
|
2353
|
+
releaseCheck?: ReleaseCheckPurpose | null;
|
|
2342
2354
|
}): void;
|
|
2343
2355
|
/**
|
|
2344
2356
|
* Re-file a scope under its OWN current words (v47): the route is
|
|
@@ -6600,6 +6612,9 @@ export declare class Store {
|
|
|
6600
6612
|
};
|
|
6601
6613
|
heartbeatWatchLease(runner: string, repo: string, owner: string, ttlMs: number, now: Date): boolean;
|
|
6602
6614
|
releaseWatchLease(runner: string, repo: string, owner: string, now: Date): void;
|
|
6615
|
+
/** Lapse every watch lease a runner holds now: its holder was proved gone (runner.ts registerRunnerIfIdle). The rows
|
|
6616
|
+
* stay, so the next acquire takes them over and recovers the superseded incarnation as after any crash. */
|
|
6617
|
+
expireWatchLeasesOf(runner: string, now: Date): number;
|
|
6603
6618
|
/** The first still-unexpired watch lease this runner holds, if any (arc 2:
|
|
6604
6619
|
* the takeover door refuses while one exists). */
|
|
6605
6620
|
liveWatchLeaseOf(runner: string, now: Date): {
|
|
@@ -7780,3 +7795,121 @@ export type AgentsLiveRun = Run & {
|
|
|
7780
7795
|
* Visibility is the caller's: it filters each by the console's ceiling and the viewer's access.
|
|
7781
7796
|
*/
|
|
7782
7797
|
export declare function agentsLiveRuns(store: Store, endedSince: Date): AgentsLiveRun[];
|
|
7798
|
+
/**
|
|
7799
|
+
* This runner's watches a predecessor left needing recovery (Codex review round 1, finding 2): a watch lease still
|
|
7800
|
+
* live, or one whose holder left a claim open (or a run open under a claim a reaper already released). A replacement
|
|
7801
|
+
* worker takes each over — recovering the holder's incarnation in the same transaction — before it admits new work,
|
|
7802
|
+
* whichever projects its start names (a project being removed when the predecessor died is in none of them).
|
|
7803
|
+
*/
|
|
7804
|
+
export declare function predecessorWatches(store: Store, runner: string, now: Date): {
|
|
7805
|
+
repo: string;
|
|
7806
|
+
owner: string;
|
|
7807
|
+
}[];
|
|
7808
|
+
/** The longest a watch lease is taken or renewed for (durable-actors' MAX_HOST_LEASE_DURATION_MS). */
|
|
7809
|
+
export declare const MAX_WATCH_LEASE_MS = 60000;
|
|
7810
|
+
/** A service component's lifecycle (durable-actors' host lifecycle reports): started, then heartbeats, then draining
|
|
7811
|
+
* (no new admission; its own drain ends by drain_until), then stopped (retired). */
|
|
7812
|
+
export type LifecycleState = "started" | "draining" | "stopped";
|
|
7813
|
+
export type LifecycleRow = {
|
|
7814
|
+
component: string;
|
|
7815
|
+
state: LifecycleState;
|
|
7816
|
+
holder: string;
|
|
7817
|
+
pid: number | null;
|
|
7818
|
+
changedAt: string;
|
|
7819
|
+
beatAt: string;
|
|
7820
|
+
drainUntil: string | null;
|
|
7821
|
+
};
|
|
7822
|
+
/** One component's lifecycle row, or null (never reported: a component from before v137 says nothing). */
|
|
7823
|
+
export declare function lifecycleOf(store: Store, component: string): LifecycleRow | null;
|
|
7824
|
+
/** Every reported component, by name. */
|
|
7825
|
+
export declare function lifecycles(store: Store): LifecycleRow[];
|
|
7826
|
+
/**
|
|
7827
|
+
* Report a component's state. `started` replaces whatever an earlier holder left; `draining` and `stopped` only move the
|
|
7828
|
+
* current holder's row (a stale process can't mark its successor draining), and never back from stopped. Returns
|
|
7829
|
+
* whether the row now says what was asked.
|
|
7830
|
+
*/
|
|
7831
|
+
export declare function reportLifecycle(store: Store, component: string, state: LifecycleState, holder: string, now: Date, extra?: {
|
|
7832
|
+
pid?: number | null;
|
|
7833
|
+
drainUntil?: Date | null;
|
|
7834
|
+
}): boolean;
|
|
7835
|
+
/** The short heartbeat: the current holder's row, while it is not stopped. False means another holder took it. */
|
|
7836
|
+
export declare function beatLifecycle(store: Store, component: string, holder: string, now: Date): boolean;
|
|
7837
|
+
/** A milestone in a latency timeline: what happened, how long after the operation began (ms), and an optional short
|
|
7838
|
+
* reason. Never a secret: route ids, phase and part names, fixed reason words. */
|
|
7839
|
+
export type Milestone = {
|
|
7840
|
+
name: string;
|
|
7841
|
+
ms: number;
|
|
7842
|
+
detail?: string;
|
|
7843
|
+
};
|
|
7844
|
+
export type LatencyKind = "console" | "release-check" | "deploy";
|
|
7845
|
+
export type LatencyTimeline = {
|
|
7846
|
+
id: number;
|
|
7847
|
+
kind: LatencyKind;
|
|
7848
|
+
subject: string;
|
|
7849
|
+
taskId: string | null;
|
|
7850
|
+
runId: number | null;
|
|
7851
|
+
startedAt: string;
|
|
7852
|
+
totalMs: number;
|
|
7853
|
+
milestones: Milestone[];
|
|
7854
|
+
};
|
|
7855
|
+
/** At most this many milestones per timeline (the rest are dropped, the last kept), and timelines kept per kind. */
|
|
7856
|
+
export declare const MAX_MILESTONES = 40;
|
|
7857
|
+
export declare const TIMELINES_KEPT = 200;
|
|
7858
|
+
/** Timelines older than this go whatever their count. */
|
|
7859
|
+
export declare const TIMELINE_DAYS = 14;
|
|
7860
|
+
/** Milestones made safe to keep: bounded in number and length, offsets whole non-negative milliseconds, in order. */
|
|
7861
|
+
export declare function boundedMilestones(milestones: readonly Milestone[]): Milestone[];
|
|
7862
|
+
/** Keep one timeline, then prune its kind to the newest TIMELINES_KEPT within TIMELINE_DAYS. */
|
|
7863
|
+
export declare function recordLatencyTimeline(store: Store, entry: {
|
|
7864
|
+
kind: LatencyKind;
|
|
7865
|
+
subject: string;
|
|
7866
|
+
taskId?: string | null;
|
|
7867
|
+
runId?: number | null;
|
|
7868
|
+
startedAt: Date;
|
|
7869
|
+
totalMs: number;
|
|
7870
|
+
milestones: readonly Milestone[];
|
|
7871
|
+
}, now: Date): number;
|
|
7872
|
+
/** Timelines, newest first: of one kind, one run's, or one task's. A row that doesn't read is left out. */
|
|
7873
|
+
export declare function latencyTimelines(store: Store, filter: {
|
|
7874
|
+
kind?: LatencyKind;
|
|
7875
|
+
runId?: number;
|
|
7876
|
+
taskId?: string;
|
|
7877
|
+
limit?: number;
|
|
7878
|
+
}): LatencyTimeline[];
|
|
7879
|
+
/** The continuous copy (live-backup.ts): how often, how many, where (null: `backups/live` beside the database). */
|
|
7880
|
+
export type LiveBackupSettings = {
|
|
7881
|
+
enabled: boolean;
|
|
7882
|
+
everyMinutes: number;
|
|
7883
|
+
keep: number;
|
|
7884
|
+
folder: string | null;
|
|
7885
|
+
};
|
|
7886
|
+
export declare const DEFAULT_LIVE_BACKUP: LiveBackupSettings;
|
|
7887
|
+
/** The latest copy that passed its integrity check, as it was when it was made. */
|
|
7888
|
+
export type LiveBackupCopy = {
|
|
7889
|
+
file: string;
|
|
7890
|
+
sha256: string;
|
|
7891
|
+
madeAt: string;
|
|
7892
|
+
schemaVersion: number;
|
|
7893
|
+
bytes: number;
|
|
7894
|
+
};
|
|
7895
|
+
export type LiveBackupState = {
|
|
7896
|
+
settings: LiveBackupSettings;
|
|
7897
|
+
latest: LiveBackupCopy | null;
|
|
7898
|
+
triedAt: string | null;
|
|
7899
|
+
problem: string | null;
|
|
7900
|
+
};
|
|
7901
|
+
export declare function liveBackupState(store: Store): LiveBackupState;
|
|
7902
|
+
/** Change the continuous copy's settings; the ledger keeps before → after. A new folder forgets the latest copy (it is
|
|
7903
|
+
* in the old one). */
|
|
7904
|
+
export declare function setLiveBackupSettings(store: Store, next: LiveBackupSettings, by: string, now: Date): LiveBackupSettings;
|
|
7905
|
+
/** Take (or keep) the continuous copy's lease until `until`: one process copies at a time. */
|
|
7906
|
+
export declare function holdLiveBackup(store: Store, holder: string, now: Date, until: Date): boolean;
|
|
7907
|
+
export declare function releaseLiveBackup(store: Store, holder: string): void;
|
|
7908
|
+
/** A copy's outcome: a verified copy becomes the latest; a failure is kept as the problem (the latest stays). */
|
|
7909
|
+
export declare function recordLiveBackup(store: Store, outcome: {
|
|
7910
|
+
ok: true;
|
|
7911
|
+
copy: LiveBackupCopy;
|
|
7912
|
+
} | {
|
|
7913
|
+
ok: false;
|
|
7914
|
+
problem: string;
|
|
7915
|
+
}, now: Date): void;
|