toolroll 0.9.86 → 0.9.88
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 +86 -0
- package/dist/acp-check.d.ts +9 -0
- package/dist/acp-check.js +40 -28
- package/dist/agents-board.d.ts +12 -0
- package/dist/agents-board.js +26 -0
- package/dist/approval-rules-ui.d.ts +4 -1
- package/dist/approval-rules-ui.js +31 -15
- package/dist/assignment-ui.js +1 -1
- package/dist/backup-ui.d.ts +5 -4
- package/dist/backup-ui.js +65 -32
- package/dist/browser/terminal.css +1 -1
- package/dist/browser/terminal.js +57 -54
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +35 -70
- package/dist/browser-crew.js +1 -1
- package/dist/browser-workspace.d.ts +329 -183
- package/dist/browser-workspace.js +10 -9
- package/dist/chat-controls.d.ts +1 -1
- package/dist/chat-controls.js +1 -1
- package/dist/chat-settings-page.d.ts +29 -9
- package/dist/chat-settings-page.js +59 -28
- package/dist/check-levels-ui.d.ts +2 -1
- package/dist/check-levels-ui.js +67 -58
- package/dist/contracts/cli.d.ts +43 -0
- package/dist/contracts/cli.js +4 -0
- package/dist/contracts/console-api.d.ts +1 -1
- package/dist/contracts/console-api.js +1 -1
- package/dist/credentials-ui.d.ts +5 -3
- package/dist/credentials-ui.js +58 -31
- package/dist/design/kit.css +722 -0
- package/dist/design/kit.d.ts +208 -0
- package/dist/design/kit.js +177 -0
- package/dist/design/tokens.css +31 -1
- package/dist/discord-settings.js +8 -4
- package/dist/export-ui.d.ts +6 -4
- package/dist/export-ui.js +18 -8
- package/dist/flow-changed.d.ts +14 -0
- package/dist/flow-changed.js +53 -0
- package/dist/flow-gallery-ui.d.ts +41 -4
- package/dist/flow-gallery-ui.js +95 -63
- package/dist/flow-starters-ui.d.ts +3 -9
- package/dist/flow-starters-ui.js +12 -23
- package/dist/flow-unattended-words.d.ts +20 -0
- package/dist/flow-unattended-words.js +24 -0
- package/dist/flow-unattended.d.ts +9 -5
- package/dist/flow-unattended.js +45 -23
- package/dist/flows-cli.js +7 -3
- package/dist/flows-ui.d.ts +12 -6
- package/dist/flows-ui.js +141 -30
- package/dist/integrations-ui.d.ts +6 -4
- package/dist/integrations-ui.js +34 -40
- package/dist/integrations.d.ts +2 -2
- package/dist/integrations.js +4 -4
- package/dist/kits-ui.d.ts +8 -10
- package/dist/kits-ui.js +29 -33
- package/dist/kits.js +1 -1
- package/dist/knowledge-ui.js +76 -19
- package/dist/ledger-view.d.ts +2 -0
- package/dist/ledger-view.js +41 -38
- package/dist/models-ui.d.ts +8 -3
- package/dist/models-ui.js +82 -59
- package/dist/monitoring-ui.d.ts +4 -2
- package/dist/monitoring-ui.js +44 -13
- package/dist/needs-you.d.ts +1 -1
- package/dist/needs-you.js +8 -0
- package/dist/operate-remote-arguments.js +1 -0
- package/dist/operate-remote.js +1 -1
- package/dist/operate.d.ts +1 -1
- package/dist/operate.js +38 -4
- package/dist/passwordless.d.ts +1 -1
- package/dist/passwordless.js +1 -1
- package/dist/people-audit-ui.d.ts +2 -1
- package/dist/people-audit-ui.js +25 -23
- package/dist/policy-ui.d.ts +3 -2
- package/dist/policy-ui.js +27 -21
- package/dist/policy.d.ts +3 -0
- package/dist/policy.js +14 -1
- package/dist/project-delete-ui.d.ts +4 -2
- package/dist/project-delete-ui.js +135 -126
- package/dist/project-knowledge.d.ts +10 -10
- package/dist/publish.d.ts +11 -0
- package/dist/publish.js +16 -3
- package/dist/pull-request-ui.d.ts +2 -1
- package/dist/pull-request-ui.js +39 -28
- package/dist/recipe-ui.d.ts +4 -2
- package/dist/recipe-ui.js +14 -4
- package/dist/release-adapters.d.ts +3 -0
- package/dist/release-adapters.js +22 -2
- package/dist/release-cli.js +81 -9
- package/dist/release.d.ts +23 -0
- package/dist/release.js +46 -4
- package/dist/releases.js +1 -1
- package/dist/request-budget-ui.d.ts +4 -2
- package/dist/request-budget-ui.js +30 -19
- package/dist/retention-ui.d.ts +4 -3
- package/dist/retention-ui.js +15 -12
- package/dist/scope.d.ts +3 -0
- package/dist/scope.js +6 -0
- package/dist/serve.d.ts +0 -1
- package/dist/serve.js +51 -7
- package/dist/server/agents-computers.d.ts +9 -0
- package/dist/server/agents-computers.js +67 -0
- package/dist/server/chat.js +16 -16
- package/dist/server/chrome.d.ts +1 -1
- package/dist/server/chrome.js +19 -9
- package/dist/server/console.css +24 -12
- package/dist/server/flows.js +64 -36
- package/dist/server/home.d.ts +35 -0
- package/dist/server/home.js +104 -0
- package/dist/server/http.d.ts +3 -0
- package/dist/server/http.js +5 -0
- package/dist/server/kit-standalone.d.ts +22 -0
- package/dist/server/kit-standalone.js +70 -0
- package/dist/server/kit.d.ts +419 -0
- package/dist/server/kit.js +369 -0
- package/dist/server/pages.d.ts +2 -1
- package/dist/server/pages.js +213 -174
- package/dist/server/people-tokens.js +26 -85
- package/dist/server/render-history.d.ts +51 -0
- package/dist/server/render-history.js +228 -0
- package/dist/server/render-new-task.d.ts +29 -0
- package/dist/server/render-new-task.js +96 -0
- package/dist/server/render-next.d.ts +63 -0
- package/dist/server/render-next.js +109 -0
- package/dist/server/render-pages.d.ts +22 -190
- package/dist/server/render-pages.js +123 -575
- package/dist/server/render-people.d.ts +24 -3
- package/dist/server/render-people.js +93 -72
- package/dist/server/render-projects.d.ts +103 -0
- package/dist/server/render-projects.js +247 -0
- package/dist/server/render-results.d.ts +5 -24
- package/dist/server/render-results.js +48 -185
- package/dist/server/render-settings.d.ts +5 -74
- package/dist/server/render-settings.js +102 -493
- package/dist/server/render-tasks.d.ts +35 -5
- package/dist/server/render-tasks.js +114 -85
- package/dist/server/route-table.js +20 -5
- package/dist/server/settings.js +276 -255
- package/dist/server/task-views.d.ts +8 -0
- package/dist/server/task-views.js +85 -2
- package/dist/server/tasks.d.ts +8 -0
- package/dist/server/tasks.js +95 -40
- package/dist/server/terminals.js +1 -1
- package/dist/server/welcome.d.ts +22 -0
- package/dist/server/welcome.js +69 -0
- package/dist/settings-groups-ui.d.ts +279 -0
- package/dist/settings-groups-ui.js +615 -0
- package/dist/settings-kit.d.ts +42 -6
- package/dist/settings-kit.js +69 -7
- package/dist/setup-guide.d.ts +2 -2
- package/dist/skills-ui.js +59 -7
- package/dist/slack-settings.js +12 -5
- package/dist/spend-ui.d.ts +45 -26
- package/dist/spend-ui.js +124 -56
- package/dist/sso-ui.d.ts +5 -2
- package/dist/sso-ui.js +63 -31
- package/dist/storage-ui.d.ts +3 -2
- package/dist/storage-ui.js +56 -50
- package/dist/store.d.ts +112 -3
- package/dist/store.js +292 -20
- package/dist/surface.js +5 -1
- package/dist/teams-settings.d.ts +2 -1
- package/dist/teams-settings.js +24 -41
- package/dist/telegram-settings.d.ts +0 -4
- package/dist/telegram-settings.js +52 -25
- package/dist/toolroll-update-ui.d.ts +8 -4
- package/dist/toolroll-update-ui.js +55 -45
- package/dist/tools-ui.d.ts +5 -3
- package/dist/tools-ui.js +107 -42
- package/dist/work-index.js +11 -7
- package/dist/workspace-ui.d.ts +1 -1
- package/dist/workspace-ui.js +7 -4
- package/package.json +1 -1
|
@@ -17,12 +17,11 @@ import { type CriterionMatrixRow, type ProofVerdict } from "../proof.js";
|
|
|
17
17
|
import { type Phase } from "../provider.js";
|
|
18
18
|
import { type QualityMode } from "../quality.js";
|
|
19
19
|
import { type AcceptanceCriterion, type Scope, type UnattendedPermissionMode } from "../scope.js";
|
|
20
|
-
import { type
|
|
20
|
+
import { type CheckProgress, type CoordinatorProposal, type Decision, type ExternalMirror, type Hold, type Incident, type PlanRevisionKind, type PlanRevisionStatus, type Publication, type PublicationGrant, type RepairChainRow, type ReviewRetryState, type RevisionLineage, type Run, type SteerNote, type Store, type Task } from "../store.js";
|
|
21
21
|
import { type TaskControlView } from "../task-control.js";
|
|
22
22
|
import { type DisplayStatus, type PublicationFacts, type ReviewFacts, type WorkStatus } from "../workspace-ui.js";
|
|
23
23
|
import { type Chrome, type Screen } from "./chrome.js";
|
|
24
24
|
import { type CompletionReceiptView, type EvidenceLinkMap, type TaskPullRequest } from "./render-results.js";
|
|
25
|
-
import { type Who } from "./session.js";
|
|
26
25
|
import { type TaskOrigin } from "../task-source.js";
|
|
27
26
|
/** What the task and approval views show about a drafted plan's contract
|
|
28
27
|
* (contract handoff, task 1). A problem is a named problem, never a blank. */
|
|
@@ -75,7 +74,7 @@ export declare function revisionSentence(revision: Extract<RevisionView, {
|
|
|
75
74
|
/** A planner's amendment, said plainly and kept in view: approving binds
|
|
76
75
|
* the amended terms, so this is never folded away. */
|
|
77
76
|
export declare function approvalAmendmentHtml(view: PlanContractView | null | undefined): Html;
|
|
78
|
-
export
|
|
77
|
+
export type ApprovalSheetInput = {
|
|
79
78
|
surface: "task" | "chat";
|
|
80
79
|
action: string;
|
|
81
80
|
csrf: string;
|
|
@@ -120,7 +119,24 @@ export declare function approvalSheetHtml(input: {
|
|
|
120
119
|
} | null;
|
|
121
120
|
/** Cockpit B2: this person's password is remembered (when, ISO) and these terms may stand on it. */
|
|
122
121
|
freshSince?: string | null;
|
|
123
|
-
}
|
|
122
|
+
};
|
|
123
|
+
/** The approval sheet: one form that restates every term it signs, the password, and Approve & start. */
|
|
124
|
+
export declare function approvalSheetHtml(input: ApprovalSheetInput): Html;
|
|
125
|
+
/** The approval sheet's parts: what it says first, the signed rows, the in-place editor and its own form, the
|
|
126
|
+
* details fold, and the words the act and its outcome use. The task page (src/design/KIT.md) shows the rows in its
|
|
127
|
+
* Plan tab and the act in its approval box; the chat and /next keep the one sheet. */
|
|
128
|
+
export declare function approvalSheetParts(input: ApprovalSheetInput): {
|
|
129
|
+
lead: Html;
|
|
130
|
+
rows: Html;
|
|
131
|
+
editor: Html | null;
|
|
132
|
+
editorForm: Html | string;
|
|
133
|
+
details: Html;
|
|
134
|
+
submit: Html;
|
|
135
|
+
label: string;
|
|
136
|
+
after: Html;
|
|
137
|
+
allowing: string[];
|
|
138
|
+
passwordNote: string;
|
|
139
|
+
};
|
|
124
140
|
/** Earlier versions of a task still in flight, in plain words: queued
|
|
125
141
|
* while they only wait, running once one has started; null when none. */
|
|
126
142
|
export declare function earlierVersionsWords(active: number, running: number): string | null;
|
|
@@ -693,9 +709,23 @@ export declare function qualityModeChoices(name: string, selected: QualityMode):
|
|
|
693
709
|
* writes the field); kept for callers.
|
|
694
710
|
*/
|
|
695
711
|
export declare function decisionOptionForms(decision: Decision, returnTo: string | null): Html;
|
|
696
|
-
export declare function decisionPage(chrome: Chrome, decision: Decision, taskId: string, artifacts: Artifact[], who: Who, now: Date, returnTo?: string | null): Screen;
|
|
697
712
|
export declare function taskOf(store: Store, decision: Decision): string;
|
|
698
713
|
/** A release task's own card (release-once.md, "What people see"): the release it's approved for, the later commits
|
|
699
714
|
* carried under it (on demand), what a later check must run again, the last nightly check of main, and one action to
|
|
700
715
|
* stop carrying. A carried task says whose approval it was carried from. */
|
|
701
716
|
export declare function releaseCardHtml(taskId: string, release: ReleaseView | null, canStop: boolean): Html;
|
|
717
|
+
/** The task page's approval box (src/design/KIT.md): what it asks, the act's own form fields, and the facts the
|
|
718
|
+
* approve control reads to show a password, one click, or nothing. The rows it signs go to the Plan tab (`planHtml`).
|
|
719
|
+
* These decide only what the page shows: the approve route proves the person, the freshness and the digest again. */
|
|
720
|
+
export declare function approveKitOf(input: ApprovalSheetInput, freshSince: string | null): NonNullable<BrowserTaskView["approve"]>;
|
|
721
|
+
/** How an approved plan was approved, for the status box's terms line: by whom and how, or the rule that approved it. */
|
|
722
|
+
export declare function approvedKitOf(scope: Scope, approval: {
|
|
723
|
+
approved: true;
|
|
724
|
+
at: string;
|
|
725
|
+
by: string;
|
|
726
|
+
} | {
|
|
727
|
+
approved: false;
|
|
728
|
+
}, proof: {
|
|
729
|
+
method: string;
|
|
730
|
+
at: string;
|
|
731
|
+
} | null): NonNullable<BrowserTaskView["approved"]> | null;
|
|
@@ -8,7 +8,7 @@ import { writesWords } from "../writes-mode.js";
|
|
|
8
8
|
import { checkAccessWords } from "../check-access.js";
|
|
9
9
|
import { sandboxTermsWords } from "../project-sandbox.js";
|
|
10
10
|
import { ACP_AGENTS, isAcpProfile } from "../acp-agents.js";
|
|
11
|
-
import { html, htmlString, joinHtml, postForm, replaceMarkup, reauth } from "../html.js";
|
|
11
|
+
import { html, htmlString, joinHtml, passwordlessRendering, postForm, replaceMarkup, reauth } from "../html.js";
|
|
12
12
|
import { closedQuestionReason, closedQuestionWords } from "../decision.js";
|
|
13
13
|
import {} from "../activity-line.js";
|
|
14
14
|
import {} from "../agentconfig.js";
|
|
@@ -38,7 +38,7 @@ import { agentModelName } from "../model-names.js";
|
|
|
38
38
|
import {} from "../provider.js";
|
|
39
39
|
import { qualityModeTitle } from "../quality.js";
|
|
40
40
|
import { evidenceShortenedWords } from "../result-review.js";
|
|
41
|
-
import { acceptanceToLines, approvalOf, scopeAuthorityOf } from "../scope.js";
|
|
41
|
+
import { acceptanceToLines, approvalOf, freshUntil, scopeAuthorityOf } from "../scope.js";
|
|
42
42
|
import {} from "../store.js";
|
|
43
43
|
import { runCostWords } from "../summary.js";
|
|
44
44
|
import {} from "../task-control.js";
|
|
@@ -230,7 +230,34 @@ function originParts(origin) {
|
|
|
230
230
|
}
|
|
231
231
|
return parts;
|
|
232
232
|
}
|
|
233
|
+
/** The approval sheet: one form that restates every term it signs, the password, and Approve & start. */
|
|
233
234
|
export function approvalSheetHtml(input) {
|
|
235
|
+
const parts = approvalSheetParts(input);
|
|
236
|
+
return joinHtml([
|
|
237
|
+
postForm(input.action, joinHtml([
|
|
238
|
+
html `<input type="text" name="username" autocomplete="username" class="visually-hidden" tabindex="-1" aria-hidden="true">`,
|
|
239
|
+
parts.lead,
|
|
240
|
+
parts.rows,
|
|
241
|
+
html `<div class="approval-act" id="${input.surface === "task" ? "approval-confirm" : "chat-approval-confirm"}">`,
|
|
242
|
+
html `${reauth(html `<label class="approval-password"><span class="visually-hidden">Your password</span><input type="password" name="token" autocomplete="current-password" placeholder="Your password" aria-describedby="${parts.passwordNote}"></label>`)}`,
|
|
243
|
+
input.freshSince == null ? html `<p class="approval-password-note" id="${parts.passwordNote}">Your password signs this approval.</p>`
|
|
244
|
+
: html `<p class="approval-password-note" id="${parts.passwordNote}" data-approval-fresh>Your password from ${whenTime(input.freshSince)} is remembered on this computer — leave this blank to approve with it, or type it again.</p>`,
|
|
245
|
+
html `<button type="submit" data-primary-action>${parts.submit}</button></div>`,
|
|
246
|
+
html `<p class="approval-after">${parts.after}</p>`,
|
|
247
|
+
html `<div class="approval-secondary">${parts.editor ?? html `<a class="approval-link" href="${input.editHref}">Edit plan</a>`}<a class="approval-link" href="${input.notNowHref}">Not now</a></div>`,
|
|
248
|
+
parts.details,
|
|
249
|
+
]), {
|
|
250
|
+
attrs: { class: "approve-form approval-sheet", id: input.surface === "task" ? "approve" : null, "data-sticky": input.sticky },
|
|
251
|
+
hidden: { nonce: input.nonce, digest: input.digest, ...(input.rebuild == null ? {} : { rebuild: String(input.rebuild) }) },
|
|
252
|
+
returnTo: input.returnTo,
|
|
253
|
+
}),
|
|
254
|
+
parts.editorForm,
|
|
255
|
+
]);
|
|
256
|
+
}
|
|
257
|
+
/** The approval sheet's parts: what it says first, the signed rows, the in-place editor and its own form, the
|
|
258
|
+
* details fold, and the words the act and its outcome use. The task page (src/design/KIT.md) shows the rows in its
|
|
259
|
+
* Plan tab and the act in its approval box; the chat and /next keep the one sheet. */
|
|
260
|
+
export function approvalSheetParts(input) {
|
|
234
261
|
const { scope, route } = input;
|
|
235
262
|
// A machine-drafted repair appends its brief to the signed goal; the row
|
|
236
263
|
// shows the goal and one sentence says what the repair fixes. The whole
|
|
@@ -392,40 +419,27 @@ export function approvalSheetHtml(input) {
|
|
|
392
419
|
"budget-microusd": scope.budgetMicrousd === null ? "none" : String(scope.budgetMicrousd),
|
|
393
420
|
},
|
|
394
421
|
});
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
input.deliverable === "report" ? html `<p class="approval-note">Read-only: it reports back and changes nothing in the repository.</p>` : "",
|
|
402
|
-
originNote(input.origin) === null ? "" : html `<p class="approval-note" data-approval-origin>${originNote(input.origin)}</p>`,
|
|
403
|
-
html `<dl class="approval-rows">`,
|
|
404
|
-
row("Goal", html `<p class="approval-goal">${goalShown}</p>`),
|
|
405
|
-
row("Changes", changes),
|
|
406
|
-
row("Won’t touch", html `<p>${scope.outOfScope === null ? "Nothing is ruled out." : scope.outOfScope}</p>`),
|
|
407
|
-
row("Done when", doneWhen),
|
|
408
|
-
html `</dl>`,
|
|
409
|
-
amendment,
|
|
410
|
-
// The size, said once and plainly beside who builds: "Small change: fast model, no plan".
|
|
411
|
-
projection === null ? "" : sizeLineHtml(projection),
|
|
412
|
-
who.length === 0 ? "" : html `<p class="approval-who">${who.join(" · ")}</p>`,
|
|
413
|
-
consent,
|
|
414
|
-
html `<div class="approval-act" id="${input.surface === "task" ? "approval-confirm" : "chat-approval-confirm"}">`,
|
|
415
|
-
html `${reauth(html `<label class="approval-password"><span class="visually-hidden">Your password</span><input type="password" name="token" autocomplete="current-password" placeholder="Your password" aria-describedby="${passwordNote}"></label>`)}`,
|
|
416
|
-
input.freshSince == null ? html `<p class="approval-password-note" id="${passwordNote}">Your password signs this approval.</p>`
|
|
417
|
-
: html `<p class="approval-password-note" id="${passwordNote}" data-approval-fresh>Your password from ${whenTime(input.freshSince)} is remembered on this computer — leave this blank to approve with it, or type it again.</p>`,
|
|
418
|
-
html `<button type="submit" data-primary-action>${submit}</button></div>`,
|
|
419
|
-
html `<p class="approval-after">${after}</p>`,
|
|
420
|
-
html `<div class="approval-secondary">${editor ?? html `<a class="approval-link" href="${input.editHref}">Edit plan</a>`}<a class="approval-link" href="${input.notNowHref}">Not now</a></div>`,
|
|
421
|
-
details,
|
|
422
|
-
]), {
|
|
423
|
-
attrs: { class: "approve-form approval-sheet", id: input.surface === "task" ? "approve" : null, "data-sticky": input.sticky },
|
|
424
|
-
hidden: { nonce: input.nonce, digest: input.digest, ...(rebuild === null ? {} : { rebuild: String(rebuild) }) },
|
|
425
|
-
returnTo: input.returnTo,
|
|
426
|
-
}),
|
|
427
|
-
editorForm,
|
|
422
|
+
const lead = joinHtml([
|
|
423
|
+
html `<h2 class="approval-sheet-title">${input.deliverable === "report" ? "The investigation" : "The plan"}</h2>`,
|
|
424
|
+
sentence === null ? "" : html `<p class="approval-revision">${sentence}</p>`,
|
|
425
|
+
input.coordinator === null ? "" : html `<p class="approval-note">An agent filed this: <span class="mono">${input.coordinator.label}</span>${input.coordinator.filedAgo === null ? "" : html `, ${input.coordinator.filedAgo}`}. Nothing runs until you approve, and approving runs its request.</p>`,
|
|
426
|
+
input.deliverable === "report" ? html `<p class="approval-note">Read-only: it reports back and changes nothing in the repository.</p>` : "",
|
|
427
|
+
originNote(input.origin) === null ? "" : html `<p class="approval-note" data-approval-origin>${originNote(input.origin)}</p>`,
|
|
428
428
|
]);
|
|
429
|
+
const rows = joinHtml([
|
|
430
|
+
html `<dl class="approval-rows">`,
|
|
431
|
+
row("Goal", html `<p class="approval-goal">${goalShown}</p>`),
|
|
432
|
+
row("Changes", changes),
|
|
433
|
+
row("Won’t touch", html `<p>${scope.outOfScope === null ? "Nothing is ruled out." : scope.outOfScope}</p>`),
|
|
434
|
+
row("Done when", doneWhen),
|
|
435
|
+
html `</dl>`,
|
|
436
|
+
amendment,
|
|
437
|
+
// The size, said once and plainly beside who builds: "Small change: fast model, no plan".
|
|
438
|
+
projection === null ? "" : sizeLineHtml(projection),
|
|
439
|
+
who.length === 0 ? "" : html `<p class="approval-who">${who.join(" · ")}</p>`,
|
|
440
|
+
consent,
|
|
441
|
+
]);
|
|
442
|
+
return { lead, rows, editor, editorForm, details, submit, label: rebuild !== null ? "Approve and build again" : "Approve and start", after, allowing, passwordNote };
|
|
429
443
|
}
|
|
430
444
|
/** Earlier versions of a task still in flight, in plain words: queued
|
|
431
445
|
* while they only wait, running once one has started; null when none. */
|
|
@@ -1627,6 +1641,36 @@ export function taskBodyParts(data) {
|
|
|
1627
1641
|
// assignment's one action): then it is shown open under the status, never folded.
|
|
1628
1642
|
const approvalLeads = data.dispatch?.action === "approve-scope" ||
|
|
1629
1643
|
(data.assignment?.primaryAction?.code === "approve-scope" && data.assignment.primaryAction.target.taskId === task.id);
|
|
1644
|
+
// The sheet's terms, when this person can approve them here: the task page's kit approval box (its Plan tab shows
|
|
1645
|
+
// the rows) and the legacy sheet both read these.
|
|
1646
|
+
const sheetInput = scope === null || approval.approved || data.plan === "requested" || (data.revision != null && "problem" in data.revision) ||
|
|
1647
|
+
scope.profileState === "unresolved" || !consentDoor.open ? null : {
|
|
1648
|
+
surface: "task",
|
|
1649
|
+
action: `${taskHref(task.id)}/approve`,
|
|
1650
|
+
csrf: data.csrf,
|
|
1651
|
+
nonce: data.nonce,
|
|
1652
|
+
digest: data.approvalDigest ?? scope.digest,
|
|
1653
|
+
freshSince: data.approvalFresh ?? null,
|
|
1654
|
+
returnTo: null,
|
|
1655
|
+
scope,
|
|
1656
|
+
planDocument: data.plan === "drafted" ? data.planDocument : null,
|
|
1657
|
+
planContract: data.plan === "drafted" ? data.planContract ?? null : null,
|
|
1658
|
+
revision: data.revision == null || "problem" in data.revision ? null : data.revision,
|
|
1659
|
+
revisionSourceHref: data.revision == null || "problem" in data.revision ? "" : `/r/${data.revision.sourceRun}`,
|
|
1660
|
+
repairChain: data.repairChain ?? null,
|
|
1661
|
+
route: data.route,
|
|
1662
|
+
coordinator: data.coordinator ?? null,
|
|
1663
|
+
origin: data.origin ?? null,
|
|
1664
|
+
deliverable: data.deliverable ?? "branch",
|
|
1665
|
+
rebuild: data.assignment?.need != null && "key" in data.assignment.need && data.assignment.need.key === "replan" ? data.assignment.need.build : null,
|
|
1666
|
+
editHref: data.csrf !== "" && data.planSha != null && data.planDocument !== null ? "#plan-edit" : "#scope",
|
|
1667
|
+
notNowHref: "/work",
|
|
1668
|
+
sticky: approvalLeads,
|
|
1669
|
+
earlier: { active: data.assignment?.earlierActive ?? 0, running: data.assignment?.earlierRunning ?? 0 },
|
|
1670
|
+
edit: data.csrf === "" ? null
|
|
1671
|
+
: { action: `${taskHref(task.id)}/scope`, draft: inlineScopeDraft ? data.scopeDraft ?? null : null, problem: inlineScopeDraft ? data.problem : null,
|
|
1672
|
+
open: data.editPlan === true, stepsHref: data.planSha != null && data.planDocument !== null ? "#plan-edit" : null },
|
|
1673
|
+
};
|
|
1630
1674
|
const approveForm = scope === null || approval.approved || data.plan === "requested"
|
|
1631
1675
|
? ""
|
|
1632
1676
|
: data.revision !== null && data.revision !== undefined && "problem" in data.revision
|
|
@@ -1635,33 +1679,7 @@ export function taskBodyParts(data) {
|
|
|
1635
1679
|
? html `<div class="card approve-form" id="approve"><p><strong>This task is waiting on you: its scope cannot be approved yet.</strong></p>${profileWords(scope)}<p class="ceremony-road"><a class="button-link" href="#scope">Edit the scope to fix it →</a></p></div>`
|
|
1636
1680
|
: !consentDoor.open
|
|
1637
1681
|
? consentClosedHtml(task.id, consentDoor, "task")
|
|
1638
|
-
: approvalSheetHtml(
|
|
1639
|
-
surface: "task",
|
|
1640
|
-
action: `${taskHref(task.id)}/approve`,
|
|
1641
|
-
csrf: data.csrf,
|
|
1642
|
-
nonce: data.nonce,
|
|
1643
|
-
digest: data.approvalDigest ?? scope.digest,
|
|
1644
|
-
freshSince: data.approvalFresh ?? null,
|
|
1645
|
-
returnTo: null,
|
|
1646
|
-
scope,
|
|
1647
|
-
planDocument: data.plan === "drafted" ? data.planDocument : null,
|
|
1648
|
-
planContract: data.plan === "drafted" ? data.planContract ?? null : null,
|
|
1649
|
-
revision: data.revision == null || "problem" in data.revision ? null : data.revision,
|
|
1650
|
-
revisionSourceHref: data.revision == null || "problem" in data.revision ? "" : `/r/${data.revision.sourceRun}`,
|
|
1651
|
-
repairChain: data.repairChain ?? null,
|
|
1652
|
-
route: data.route,
|
|
1653
|
-
coordinator: data.coordinator ?? null,
|
|
1654
|
-
origin: data.origin ?? null,
|
|
1655
|
-
deliverable: data.deliverable ?? "branch",
|
|
1656
|
-
rebuild: data.assignment?.need != null && "key" in data.assignment.need && data.assignment.need.key === "replan" ? data.assignment.need.build : null,
|
|
1657
|
-
editHref: data.csrf !== "" && data.planSha != null && data.planDocument !== null ? "#plan-edit" : "#scope",
|
|
1658
|
-
notNowHref: "/work",
|
|
1659
|
-
sticky: approvalLeads,
|
|
1660
|
-
earlier: { active: data.assignment?.earlierActive ?? 0, running: data.assignment?.earlierRunning ?? 0 },
|
|
1661
|
-
edit: data.csrf === "" ? null
|
|
1662
|
-
: { action: `${taskHref(task.id)}/scope`, draft: inlineScopeDraft ? data.scopeDraft ?? null : null, problem: inlineScopeDraft ? data.problem : null,
|
|
1663
|
-
open: data.editPlan === true, stepsHref: data.planSha != null && data.planDocument !== null ? "#plan-edit" : null },
|
|
1664
|
-
});
|
|
1682
|
+
: approvalSheetHtml(sheetInput);
|
|
1665
1683
|
const scopeForm = joinHtml([
|
|
1666
1684
|
html `<details${scope === null || scopeDraft !== undefined ? html ` open` : ""}><summary>${scope === null ? "Write the scope" : "Edit the scope"}${approval.approved ? " (editing voids the approval)" : ""}</summary>`,
|
|
1667
1685
|
postForm(`${taskHref(task.id)}/scope`, joinHtml([
|
|
@@ -2365,6 +2383,9 @@ export function taskBodyParts(data) {
|
|
|
2365
2383
|
earlier,
|
|
2366
2384
|
approval: data.archived != null ? "" : fragmentString(approvalHtml),
|
|
2367
2385
|
approvalPlacement: approvalLeads ? "lead" : "plan",
|
|
2386
|
+
// The kit's approval box (src/design/KIT.md): the act, by the real rules, with the rows in the Plan tab.
|
|
2387
|
+
approve: data.archived != null || sheetInput === null || dependencyChoiceNeeded || data.csrf === "" ? null : approveKitOf(sheetInput, data.approvalFresh ?? null),
|
|
2388
|
+
approved: scope !== null && approval.approved ? approvedKitOf(scope, approval, data.approvalProof ?? null) : null,
|
|
2368
2389
|
more,
|
|
2369
2390
|
confirmStopped: data.csrf !== "" && data.assignment?.primaryAction?.code === "confirm-stopped" && data.assignment.primaryAction.target.runId !== null
|
|
2370
2391
|
? { action: `${taskHref(data.assignment.rootId)}/confirm-stopped`, run: data.assignment.primaryAction.target.runId, checked: needsCheck(data.assignment) } : null,
|
|
@@ -2488,29 +2509,6 @@ export function decisionOptionForms(decision, returnTo) {
|
|
|
2488
2509
|
: html `<details class="arm-danger"><summary>${option.label} — irreversible, tap to arm</summary>${inner}</details>`;
|
|
2489
2510
|
}), "\n");
|
|
2490
2511
|
}
|
|
2491
|
-
export function decisionPage(chrome, decision, taskId, artifacts, who, now, returnTo = null) {
|
|
2492
|
-
const options = decisionOptionForms(decision, returnTo);
|
|
2493
|
-
const answered = decision.supersededReason !== null
|
|
2494
|
-
? html `<p class="meta">${closedQuestionReason(decision.supersededReason)}</p>`
|
|
2495
|
-
: decision.state === "answered"
|
|
2496
|
-
? html `<div class="answered">Answered: <strong>${decision.choice ?? ""}</strong> by ${decision.answeredBy ?? ""}${decision.note === null ? "" : ` — ${decision.note}`}</div>`
|
|
2497
|
-
: "";
|
|
2498
|
-
const evidence = artifacts.length === 0
|
|
2499
|
-
? ""
|
|
2500
|
-
: joinHtml([
|
|
2501
|
-
html `<div class="evidence"><strong>Evidence</strong>`,
|
|
2502
|
-
joinHtml(artifacts.map(artifact => html `<a href="/d/${decision.id}/evidence/${artifact.id}">${evidenceWords(artifact.kind)}${artifact.truncated ? " (truncated)" : ""} · ${artifact.bytesStored} bytes</a>`), "\n"),
|
|
2503
|
-
html `</div>`,
|
|
2504
|
-
]);
|
|
2505
|
-
return screen(`decide · ${taskId}`, joinHtml([
|
|
2506
|
-
html `<h1>${taskId} <span class="badge badge-${decision.state}">${decision.supersededReason !== null ? "closed" : decision.state}</span>${isOverdue(decision, now) ? html ` <span class="badge badge-overdue">Overdue</span>` : ""}${decision.deadline === null ? "" : html ` <span class="meta">deadline ${decision.deadline}</span>`}</h1>`,
|
|
2507
|
-
html `<div class="recap">${decision.recap}</div>`,
|
|
2508
|
-
html `<div class="question">${decision.question}</div>`,
|
|
2509
|
-
decision.state === "answered" ? answered : options,
|
|
2510
|
-
evidence,
|
|
2511
|
-
html `<p class="meta"><a href="${returnTo === null ? "/" : returnTo}">← ${returnTo === null ? "everything waiting" : "back to the task chat"}</a></p>`,
|
|
2512
|
-
], "\n"), { chrome });
|
|
2513
|
-
}
|
|
2514
2512
|
export function taskOf(store, decision) {
|
|
2515
2513
|
const run = store.getRun(decision.run);
|
|
2516
2514
|
return run === null ? "?" : store.externalIdFor(run.taskRef) ?? "?";
|
|
@@ -2538,3 +2536,34 @@ export function releaseCardHtml(taskId, release, canStop) {
|
|
|
2538
2536
|
html `</div>`,
|
|
2539
2537
|
], "\n");
|
|
2540
2538
|
}
|
|
2539
|
+
/** The task page's approval box (src/design/KIT.md): what it asks, the act's own form fields, and the facts the
|
|
2540
|
+
* approve control reads to show a password, one click, or nothing. The rows it signs go to the Plan tab (`planHtml`).
|
|
2541
|
+
* These decide only what the page shows: the approve route proves the person, the freshness and the digest again. */
|
|
2542
|
+
export function approveKitOf(input, freshSince) {
|
|
2543
|
+
const parts = approvalSheetParts(input);
|
|
2544
|
+
const rebuild = input.rebuild ?? null;
|
|
2545
|
+
const after = rebuild !== null ? "builds it again to this plan" : input.deliverable === "report" ? "starts the investigation" : "starts the build";
|
|
2546
|
+
const allowing = parts.allowing.length === 0 ? "" : ` You’re allowing ${parts.allowing.join(", ")}.`;
|
|
2547
|
+
const until = freshSince === null ? null : freshUntil(freshSince);
|
|
2548
|
+
return {
|
|
2549
|
+
action: input.action,
|
|
2550
|
+
fields: { nonce: input.nonce, digest: input.digest, ...(rebuild === null ? {} : { rebuild: String(rebuild) }) },
|
|
2551
|
+
label: parts.label,
|
|
2552
|
+
heading: rebuild !== null ? "The plan changed" : input.deliverable === "report" ? "Investigation ready" : "Plan ready",
|
|
2553
|
+
text: `Approving signs plan version ${input.scope.digest.slice(0, 12)} and ${after}.${allowing}`,
|
|
2554
|
+
kind: "standard",
|
|
2555
|
+
facts: { passwordless: passwordlessRendering(), remembered: until !== null },
|
|
2556
|
+
rememberedUntil: until,
|
|
2557
|
+
planHtml: htmlString(joinHtml([parts.lead, parts.rows, parts.editor ?? "", parts.details, parts.editorForm])),
|
|
2558
|
+
};
|
|
2559
|
+
}
|
|
2560
|
+
/** How an approved plan was approved, for the status box's terms line: by whom and how, or the rule that approved it. */
|
|
2561
|
+
export function approvedKitOf(scope, approval, proof) {
|
|
2562
|
+
if (!approval.approved)
|
|
2563
|
+
return null;
|
|
2564
|
+
const basis = scope.approvalBasis ?? null;
|
|
2565
|
+
const rule = basis === "mode" ? "your operating mode for this project" : basis === "grant" ? "its flow, which runs on its own" : basis === "schedule" ? "the schedule you approved"
|
|
2566
|
+
: basis === "auto-green" || basis === "auto-check" ? "this project’s release approval (CI and the release check passed)" : basis === "carried" ? "the release approval it was carried under" : null;
|
|
2567
|
+
const how = approvalProofWords(proof).replace(/^ \(|\)$/g, "");
|
|
2568
|
+
return { by: approval.by, at: approval.at, automatic: rule !== null, rule, how: how === "" ? (rule === null ? "password typed" : "") : how, version: scope.digest.slice(0, 12) };
|
|
2569
|
+
}
|
|
@@ -124,25 +124,30 @@ get("pages", "projects.browse", "/projects/browse", free);
|
|
|
124
124
|
get("pages", "projects.github", "/projects/github", { ...free, callers: ["cookie"], callerRefusal: { status: 403, type: "text/plain; charset=utf-8", body: "listing repositories feeds a browser session's act" },
|
|
125
125
|
...anyApprover("your login can watch — adding projects is an approver's act") });
|
|
126
126
|
get("pages", "projects.page", "/projects", { limited: "unscoped", ...free });
|
|
127
|
+
// One project's reading page (/projects/<name>): reads only, like every GET (docs/plans/get-side-effects.md). It shows a
|
|
128
|
+
// project this account can see and never switches the session's project; its Open project button posts to
|
|
129
|
+
// /projects/select. The handler resolves the name among the account's visible projects; anything else is not found.
|
|
130
|
+
get("pages", "projects.project", "^\\/projects\\/[^/]+$", { limited: "unscoped", ...free, sample: "/projects/payments-api" });
|
|
127
131
|
get("pages", "home", "/", { limited: "collection", ...free });
|
|
132
|
+
get("pages", "search", "/search", { limited: "collection", ...free });
|
|
128
133
|
get("tasks", "inbox", "/inbox", { limited: "collection", ...free });
|
|
129
134
|
get("tasks", "work", "/work", { limited: "collection", ...free });
|
|
130
135
|
// Put away old tasks (task-tidy.md): the preview reads only; putting away is an approver's step-up in a browser session.
|
|
131
136
|
get("tasks", "work.put-away", "/work/put-away", { limited: "collection", ...free, callers: ["cookie"], ...approver("Only an approver can put tasks away.", "/work") });
|
|
132
137
|
post("tasks", "work.put-away.apply", "/work/put-away", { scope: "step-up", limited: "collection", ...approver("Only an approver can put tasks away.", "/work") });
|
|
133
138
|
get("tasks", "next", "/next", { limited: "collection" });
|
|
134
|
-
get("pages", "morning", "/morning");
|
|
139
|
+
get("pages", "morning", "/morning", { limited: "collection", ...free }); // a redirect to / now
|
|
135
140
|
get("pages", "workbench", "/workbench", free);
|
|
136
141
|
get("tasks", "board", "/board", { limited: "collection" });
|
|
137
142
|
get("tasks", "review", "/review", { limited: "collection" });
|
|
138
143
|
get("settings", "spend", "/spend", { ...free, ...anyOperator("An instance operator sees spend.", "/") });
|
|
139
144
|
get("settings", "settings.spend", "/settings/spend", { ...free, ...anyOperator("An instance operator sets spending limits.", "/settings") });
|
|
140
145
|
get("pages", "ledger", "/ledger", { limited: "unscoped", ...free });
|
|
141
|
-
get("pages", "activity", "/activity");
|
|
142
|
-
get("tasks", "done", "/done", { limited: "collection" });
|
|
146
|
+
get("pages", "activity", "/activity", { limited: "collection", ...free }); // a redirect to / now (Home has the summary)
|
|
147
|
+
get("tasks", "done", "/done", { limited: "collection", ...free }); // a redirect to the Tasks list's Finished view now
|
|
143
148
|
get("settings", "mode", "/mode");
|
|
144
149
|
get("people", "people.page", "/people", { limited: "unscoped", ...free });
|
|
145
|
-
get("pages", "system", "/system");
|
|
150
|
+
get("pages", "system", "/system", free);
|
|
146
151
|
// Where every "Connect a builder" leads: read-only, open to a project-limited person (the handler shows only a visible project).
|
|
147
152
|
get("pages", "start-builder", "/start-builder", { limited: "unscoped", ...free });
|
|
148
153
|
get("tasks", "tasks", "/tasks", { limited: "collection", ...free }); // a redirect to /work now: never sent to the project opener first
|
|
@@ -153,12 +158,17 @@ get("pages", "agents", "/agents", { limited: "unscoped", ...free });
|
|
|
153
158
|
// Browser sessions only, like the transcript and the peek: no token (a lead's, a runner's, a remote one) reads live output.
|
|
154
159
|
get("pages", "agents.live", "/agents/live", { limited: "unscoped", callers: ["cookie"], ...free });
|
|
155
160
|
get("pages", "agents.live.read", `^\\/agents\\/live\\/${ID}$`, { limited: "unscoped", callers: ["cookie"], ...free, sample: "/agents/live/1" });
|
|
161
|
+
// Terminals' address in the Agents area: a 302 to /term, admitted exactly as /term is (an operator's browser session).
|
|
162
|
+
get("pages", "agents.terminals", "/agents/terminals", TERM);
|
|
163
|
+
// Fleet and System are parts of Agents now: GET aliases to /agents#computers and /agents#models.
|
|
156
164
|
get("pages", "fleet", "/fleet", free);
|
|
157
165
|
get("tasks", "tasks.new", "/tasks/new", { limited: "collection", ...free });
|
|
166
|
+
// The first run's own page (Wave E): five guided steps, reads only; it leads Home once the first run is over.
|
|
167
|
+
get("tasks", "welcome", "/welcome", { limited: "collection", ...free });
|
|
158
168
|
get("tasks", "task.page", `^\\/t\\/${TASK}$`, { project: "task", limited: "resource", ...free, sample: "/t/one" });
|
|
159
169
|
get("tasks", "task.evidence", `^\\/t\\/${TASK}\\/evidence$`, { project: "task", limited: "resource", ...free, sample: "/t/one/evidence" });
|
|
160
170
|
get("pages", "ledger.export", "/ledger/export", { limited: "unscoped", ...free });
|
|
161
|
-
get("tasks", "runs", "/runs", { limited: "collection" });
|
|
171
|
+
get("tasks", "runs", "/runs", { limited: "collection", ...free }); // a redirect to /agents now
|
|
162
172
|
get("pages", "menu", "/menu", { limited: "collection", ...free });
|
|
163
173
|
get("tasks", "run.page", `^\\/r\\/${ID}$`, { project: "run", limited: "resource", ...free, sample: "/r/1" });
|
|
164
174
|
get("tasks", "run.evidence", `^\\/r\\/${ID}\\/evidence\\/${ID}$`, { project: "run", limited: "resource", ...free, sample: "/r/1/evidence/2" });
|
|
@@ -218,6 +228,11 @@ get("settings", "settings.knowledge", "/settings/knowledge", { limited: "unscope
|
|
|
218
228
|
get("settings", "settings.learning", "/settings/learning", { limited: "unscoped", ...free });
|
|
219
229
|
get("settings", "settings.telegram", "/settings/telegram", { limited: "self", ...free, ...approver("An approver can pair their own phone.", "/settings") });
|
|
220
230
|
get("settings", "settings.page", "/settings", { limited: "unscoped", ...free });
|
|
231
|
+
// The Settings groups (docs/plans/settings-revamp.md §3): one read-only page each, drawn by the overview's handler.
|
|
232
|
+
// Anyone signed in may open one; the page shows only the sections this person can use, and every form's own route
|
|
233
|
+
// keeps its own role and proof.
|
|
234
|
+
for (const one of ["general", "agents", "alerts", "projects", "access", "system"])
|
|
235
|
+
get("settings", `settings.group-${one}`, `/settings/${one}`, { limited: "unscoped", ...free });
|
|
221
236
|
get("pages", "health", "/health", { ...free, ...anyOperator("An instance operator reads server health.", "/") });
|
|
222
237
|
get("pages", "metrics", "/metrics", { ...free, ...anyOperator("An instance operator reads metrics.", "/") });
|
|
223
238
|
get("chat", "push.key", "/push/key");
|