toolroll 0.9.85 → 0.9.87
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 +83 -0
- package/dist/agent-fence.d.ts +6 -2
- package/dist/agent-fence.js +3 -2
- 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/assignment.js +4 -3
- 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 -68
- package/dist/browser-crew.js +1 -1
- package/dist/browser-workspace.d.ts +363 -177
- package/dist/browser-workspace.js +10 -9
- package/dist/chat-actions.d.ts +11 -1
- package/dist/chat-actions.js +53 -1
- package/dist/chat-channel.js +1 -0
- package/dist/chat-controls.d.ts +1 -1
- package/dist/chat-controls.js +1 -1
- package/dist/chat-quiet.js +3 -2
- 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/claim.d.ts +3 -0
- package/dist/claim.js +4 -1
- package/dist/contracts/chat-actions.d.ts +9 -0
- package/dist/contracts/chat-actions.js +3 -0
- package/dist/contracts/cli.d.ts +43 -0
- package/dist/contracts/cli.js +4 -0
- package/dist/contracts/console-api.d.ts +4 -1
- package/dist/contracts/console-api.js +4 -1
- package/dist/contracts/flow-propose.d.ts +4 -1
- package/dist/contracts/flow-propose.js +1 -1
- package/dist/contracts/flow.d.ts +41 -0
- package/dist/contracts/flow.js +4 -2
- package/dist/contracts/lead-tools.d.ts +21 -0
- package/dist/contracts/lead-tools.js +2 -0
- package/dist/contracts/store-json.d.ts +2 -0
- package/dist/contracts/trigger-payloads.d.ts +20 -0
- package/dist/contracts/trigger-payloads.js +4 -0
- package/dist/coordinator.js +1 -0
- package/dist/credentials-ui.d.ts +5 -3
- package/dist/credentials-ui.js +58 -31
- package/dist/deploy-gate.js +4 -0
- package/dist/design/kit.css +685 -0
- package/dist/design/kit.d.ts +208 -0
- package/dist/design/kit.js +177 -0
- package/dist/design/tokens.css +19 -1
- package/dist/discord-settings.js +8 -4
- package/dist/dispatch.d.ts +3 -0
- package/dist/dispatch.js +4 -1
- package/dist/export-ui.d.ts +6 -4
- package/dist/export-ui.js +18 -8
- package/dist/flow-browser.d.ts +103 -0
- package/dist/flow-browser.js +204 -0
- package/dist/flow-changed.d.ts +14 -0
- package/dist/flow-changed.js +53 -0
- package/dist/flow-code.d.ts +5 -1
- package/dist/flow-code.js +4 -1
- package/dist/flow-engine.js +16 -0
- package/dist/flow-gallery-ui.d.ts +41 -4
- package/dist/flow-gallery-ui.js +95 -63
- package/dist/flow-live.js +9 -1
- package/dist/flow-pull-request.js +15 -7
- package/dist/flow-share.js +3 -1
- package/dist/flow-starters-ui.d.ts +3 -9
- package/dist/flow-starters-ui.js +12 -23
- package/dist/flow-steps.d.ts +9 -1
- package/dist/flow-steps.js +55 -5
- package/dist/flow-triggers.js +55 -0
- 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.d.ts +19 -0
- package/dist/flows-cli.js +54 -5
- package/dist/flows-ui.d.ts +12 -6
- package/dist/flows-ui.js +152 -32
- package/dist/flows.js +5 -2
- 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/lead-tools.js +35 -6
- package/dist/lead-voice.js +16 -5
- 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 +249 -6
- 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-delete.js +7 -3
- 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-flow.js +35 -4
- 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 +21 -0
- package/dist/release.js +40 -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/result-follow-ups.js +4 -0
- package/dist/retention-ui.d.ts +4 -3
- package/dist/retention-ui.js +15 -12
- package/dist/scope.d.ts +8 -0
- package/dist/scope.js +13 -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 +20 -18
- package/dist/server/chrome.d.ts +1 -1
- package/dist/server/chrome.js +19 -9
- package/dist/server/console.css +31 -2
- package/dist/server/flows.js +79 -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 +69 -0
- package/dist/server/kit.d.ts +405 -0
- package/dist/server/kit.js +361 -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 +35 -190
- package/dist/server/render-pages.js +154 -563
- 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 +43 -5
- package/dist/server/render-tasks.js +131 -91
- package/dist/server/route-table.js +31 -5
- package/dist/server/settings.js +276 -255
- package/dist/server/task-views.d.ts +8 -0
- package/dist/server/task-views.js +90 -2
- package/dist/server/tasks.d.ts +18 -0
- package/dist/server/tasks.js +148 -41
- 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/settings-proofs.js +1 -0
- 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 +182 -5
- package/dist/store.js +512 -38
- package/dist/surface.js +23 -3
- package/dist/task-archive.d.ts +10 -0
- package/dist/task-archive.js +16 -0
- package/dist/task-control.d.ts +1 -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/tidy.d.ts +130 -0
- package/dist/tidy.js +503 -0
- 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.d.ts +24 -0
- package/dist/work-index.js +56 -30
- package/dist/workspace-ui.d.ts +1 -1
- package/dist/workspace-ui.js +8 -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;
|
|
@@ -438,6 +454,14 @@ export declare function taskBodyParts(data: {
|
|
|
438
454
|
/** Markup the caller rendered (the version history). */
|
|
439
455
|
history?: Html;
|
|
440
456
|
versionLabel?: string | null;
|
|
457
|
+
/** Put away (v145): by whom and why; and whether the viewer can put it away or restore it (an approver's browser). */
|
|
458
|
+
archived?: {
|
|
459
|
+
by: string;
|
|
460
|
+
byKind: "person" | "tidy";
|
|
461
|
+
reason: string;
|
|
462
|
+
at: string;
|
|
463
|
+
} | null;
|
|
464
|
+
canPutAway?: boolean;
|
|
441
465
|
status?: DisplayStatus;
|
|
442
466
|
task: Task;
|
|
443
467
|
/** Shared read-side lifecycle answer; the atomic claim still re-proves it. */
|
|
@@ -685,9 +709,23 @@ export declare function qualityModeChoices(name: string, selected: QualityMode):
|
|
|
685
709
|
* writes the field); kept for callers.
|
|
686
710
|
*/
|
|
687
711
|
export declare function decisionOptionForms(decision: Decision, returnTo: string | null): Html;
|
|
688
|
-
export declare function decisionPage(chrome: Chrome, decision: Decision, taskId: string, artifacts: Artifact[], who: Who, now: Date, returnTo?: string | null): Screen;
|
|
689
712
|
export declare function taskOf(store: Store, decision: Decision): string;
|
|
690
713
|
/** A release task's own card (release-once.md, "What people see"): the release it's approved for, the later commits
|
|
691
714
|
* carried under it (on demand), what a later check must run again, the last nightly check of main, and one action to
|
|
692
715
|
* stop carrying. A carried task says whose approval it was carried from. */
|
|
693
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;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
/** Task pages: approval, agents, plans, controls, decisions and the task body. */
|
|
2
|
+
import { archivedWords } from "../tidy.js";
|
|
3
|
+
import { whenUtc } from "../when-html.js";
|
|
2
4
|
import { networkWords } from "../egress.js";
|
|
3
5
|
import { releaseAsksAgainWords, releaseTermsSentence } from "../scope.js";
|
|
4
6
|
import { RELEASE_CI_RUNNING_WORDS } from "../release-ci-hold.js";
|
|
@@ -6,7 +8,7 @@ import { writesWords } from "../writes-mode.js";
|
|
|
6
8
|
import { checkAccessWords } from "../check-access.js";
|
|
7
9
|
import { sandboxTermsWords } from "../project-sandbox.js";
|
|
8
10
|
import { ACP_AGENTS, isAcpProfile } from "../acp-agents.js";
|
|
9
|
-
import { html, htmlString, joinHtml, postForm, replaceMarkup, reauth } from "../html.js";
|
|
11
|
+
import { html, htmlString, joinHtml, passwordlessRendering, postForm, replaceMarkup, reauth } from "../html.js";
|
|
10
12
|
import { closedQuestionReason, closedQuestionWords } from "../decision.js";
|
|
11
13
|
import {} from "../activity-line.js";
|
|
12
14
|
import {} from "../agentconfig.js";
|
|
@@ -36,7 +38,7 @@ import { agentModelName } from "../model-names.js";
|
|
|
36
38
|
import {} from "../provider.js";
|
|
37
39
|
import { qualityModeTitle } from "../quality.js";
|
|
38
40
|
import { evidenceShortenedWords } from "../result-review.js";
|
|
39
|
-
import { acceptanceToLines, approvalOf, scopeAuthorityOf } from "../scope.js";
|
|
41
|
+
import { acceptanceToLines, approvalOf, freshUntil, scopeAuthorityOf } from "../scope.js";
|
|
40
42
|
import {} from "../store.js";
|
|
41
43
|
import { runCostWords } from "../summary.js";
|
|
42
44
|
import {} from "../task-control.js";
|
|
@@ -228,7 +230,34 @@ function originParts(origin) {
|
|
|
228
230
|
}
|
|
229
231
|
return parts;
|
|
230
232
|
}
|
|
233
|
+
/** The approval sheet: one form that restates every term it signs, the password, and Approve & start. */
|
|
231
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) {
|
|
232
261
|
const { scope, route } = input;
|
|
233
262
|
// A machine-drafted repair appends its brief to the signed goal; the row
|
|
234
263
|
// shows the goal and one sentence says what the repair fixes. The whole
|
|
@@ -390,40 +419,27 @@ export function approvalSheetHtml(input) {
|
|
|
390
419
|
"budget-microusd": scope.budgetMicrousd === null ? "none" : String(scope.budgetMicrousd),
|
|
391
420
|
},
|
|
392
421
|
});
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
input.deliverable === "report" ? html `<p class="approval-note">Read-only: it reports back and changes nothing in the repository.</p>` : "",
|
|
400
|
-
originNote(input.origin) === null ? "" : html `<p class="approval-note" data-approval-origin>${originNote(input.origin)}</p>`,
|
|
401
|
-
html `<dl class="approval-rows">`,
|
|
402
|
-
row("Goal", html `<p class="approval-goal">${goalShown}</p>`),
|
|
403
|
-
row("Changes", changes),
|
|
404
|
-
row("Won’t touch", html `<p>${scope.outOfScope === null ? "Nothing is ruled out." : scope.outOfScope}</p>`),
|
|
405
|
-
row("Done when", doneWhen),
|
|
406
|
-
html `</dl>`,
|
|
407
|
-
amendment,
|
|
408
|
-
// The size, said once and plainly beside who builds: "Small change: fast model, no plan".
|
|
409
|
-
projection === null ? "" : sizeLineHtml(projection),
|
|
410
|
-
who.length === 0 ? "" : html `<p class="approval-who">${who.join(" · ")}</p>`,
|
|
411
|
-
consent,
|
|
412
|
-
html `<div class="approval-act" id="${input.surface === "task" ? "approval-confirm" : "chat-approval-confirm"}">`,
|
|
413
|
-
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>`)}`,
|
|
414
|
-
input.freshSince == null ? html `<p class="approval-password-note" id="${passwordNote}">Your password signs this approval.</p>`
|
|
415
|
-
: 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>`,
|
|
416
|
-
html `<button type="submit" data-primary-action>${submit}</button></div>`,
|
|
417
|
-
html `<p class="approval-after">${after}</p>`,
|
|
418
|
-
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>`,
|
|
419
|
-
details,
|
|
420
|
-
]), {
|
|
421
|
-
attrs: { class: "approve-form approval-sheet", id: input.surface === "task" ? "approve" : null, "data-sticky": input.sticky },
|
|
422
|
-
hidden: { nonce: input.nonce, digest: input.digest, ...(rebuild === null ? {} : { rebuild: String(rebuild) }) },
|
|
423
|
-
returnTo: input.returnTo,
|
|
424
|
-
}),
|
|
425
|
-
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>`,
|
|
426
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 };
|
|
427
443
|
}
|
|
428
444
|
/** Earlier versions of a task still in flight, in plain words: queued
|
|
429
445
|
* while they only wait, running once one has started; null when none. */
|
|
@@ -774,6 +790,8 @@ export function approveRefusalWords(reason) {
|
|
|
774
790
|
return gateWords({ verdict: "refuse", reason });
|
|
775
791
|
if (reason === "machine-proof")
|
|
776
792
|
return "not approved: only a person can approve this. Approve it on the task page or from your paired chat";
|
|
793
|
+
if (reason === "archived")
|
|
794
|
+
return "not approved: this task is put away. Restore it first";
|
|
777
795
|
if (reason === "unrouted")
|
|
778
796
|
return "not approved: this scope predates agent routing and its old approval no longer stands — edit and re-file the scope so it is routed under today’s agents, then approve it";
|
|
779
797
|
if (reason === "profile-unresolved")
|
|
@@ -1623,6 +1641,36 @@ export function taskBodyParts(data) {
|
|
|
1623
1641
|
// assignment's one action): then it is shown open under the status, never folded.
|
|
1624
1642
|
const approvalLeads = data.dispatch?.action === "approve-scope" ||
|
|
1625
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
|
+
};
|
|
1626
1674
|
const approveForm = scope === null || approval.approved || data.plan === "requested"
|
|
1627
1675
|
? ""
|
|
1628
1676
|
: data.revision !== null && data.revision !== undefined && "problem" in data.revision
|
|
@@ -1631,33 +1679,7 @@ export function taskBodyParts(data) {
|
|
|
1631
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>`
|
|
1632
1680
|
: !consentDoor.open
|
|
1633
1681
|
? consentClosedHtml(task.id, consentDoor, "task")
|
|
1634
|
-
: approvalSheetHtml(
|
|
1635
|
-
surface: "task",
|
|
1636
|
-
action: `${taskHref(task.id)}/approve`,
|
|
1637
|
-
csrf: data.csrf,
|
|
1638
|
-
nonce: data.nonce,
|
|
1639
|
-
digest: data.approvalDigest ?? scope.digest,
|
|
1640
|
-
freshSince: data.approvalFresh ?? null,
|
|
1641
|
-
returnTo: null,
|
|
1642
|
-
scope,
|
|
1643
|
-
planDocument: data.plan === "drafted" ? data.planDocument : null,
|
|
1644
|
-
planContract: data.plan === "drafted" ? data.planContract ?? null : null,
|
|
1645
|
-
revision: data.revision == null || "problem" in data.revision ? null : data.revision,
|
|
1646
|
-
revisionSourceHref: data.revision == null || "problem" in data.revision ? "" : `/r/${data.revision.sourceRun}`,
|
|
1647
|
-
repairChain: data.repairChain ?? null,
|
|
1648
|
-
route: data.route,
|
|
1649
|
-
coordinator: data.coordinator ?? null,
|
|
1650
|
-
origin: data.origin ?? null,
|
|
1651
|
-
deliverable: data.deliverable ?? "branch",
|
|
1652
|
-
rebuild: data.assignment?.need != null && "key" in data.assignment.need && data.assignment.need.key === "replan" ? data.assignment.need.build : null,
|
|
1653
|
-
editHref: data.csrf !== "" && data.planSha != null && data.planDocument !== null ? "#plan-edit" : "#scope",
|
|
1654
|
-
notNowHref: "/work",
|
|
1655
|
-
sticky: approvalLeads,
|
|
1656
|
-
earlier: { active: data.assignment?.earlierActive ?? 0, running: data.assignment?.earlierRunning ?? 0 },
|
|
1657
|
-
edit: data.csrf === "" ? null
|
|
1658
|
-
: { action: `${taskHref(task.id)}/scope`, draft: inlineScopeDraft ? data.scopeDraft ?? null : null, problem: inlineScopeDraft ? data.problem : null,
|
|
1659
|
-
open: data.editPlan === true, stepsHref: data.planSha != null && data.planDocument !== null ? "#plan-edit" : null },
|
|
1660
|
-
});
|
|
1682
|
+
: approvalSheetHtml(sheetInput);
|
|
1661
1683
|
const scopeForm = joinHtml([
|
|
1662
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>`,
|
|
1663
1685
|
postForm(`${taskHref(task.id)}/scope`, joinHtml([
|
|
@@ -2068,8 +2090,11 @@ export function taskBodyParts(data) {
|
|
|
2068
2090
|
// The title is bare; the receipt or shared task status leads once.
|
|
2069
2091
|
html `<div class="task-title-row"><h1 class="task-main-title">${data.rootTitle ?? task.title}</h1>${data.csrf === "" ? "" : taskViewSwitch(data.rootId ?? task.id, "overview")}</div>`,
|
|
2070
2092
|
data.versionLabel == null ? "" : html `<p class="meta">${data.versionLabel} · <a href="${taskHref(data.rootId ?? task.id)}">Current work</a></p>`,
|
|
2093
|
+
data.archived == null ? "" : html `<div class="task-archived" data-archived><p>${archivedWords(data.archived)} · ${whenUtc(data.archived.at)}</p>${data.canPutAway === true && data.csrf !== ""
|
|
2094
|
+
? postForm(`${taskHref(task.id)}/restore`, html `<button type="submit" class="secondary">Restore</button>`, { hidden: { return: taskHref(task.id) }, attrs: { class: "inline" } }) : ""}</div>`,
|
|
2071
2095
|
(data.history ?? ""),
|
|
2072
|
-
|
|
2096
|
+
// A put-away task's banner is its status: no Needs you card, no act, no approval form (each would be refused).
|
|
2097
|
+
data.archived == null ? statusHtml : "",
|
|
2073
2098
|
controlHtml,
|
|
2074
2099
|
previousResult === null ? "" : html `<details class="task-previous-result"><summary>${previousResult.title}</summary>${previousResult.html}</details>`,
|
|
2075
2100
|
progressCard,
|
|
@@ -2078,7 +2103,7 @@ export function taskBodyParts(data) {
|
|
|
2078
2103
|
mirrorCard,
|
|
2079
2104
|
problemHtml,
|
|
2080
2105
|
needsScopeCard,
|
|
2081
|
-
approvalHtml,
|
|
2106
|
+
data.archived == null ? approvalHtml : "",
|
|
2082
2107
|
html `<details class="task-status-details" id="task-diagnostics"${optionsOpen ? html ` open` : ""}><summary>Task options</summary>${optionsHtml}</details>`,
|
|
2083
2108
|
// Evidence-first (M5.5): what needs you, then what happened — decisions
|
|
2084
2109
|
// and incidents above the attempt ledger and spend, the mechanics
|
|
@@ -2209,7 +2234,8 @@ export function taskBodyParts(data) {
|
|
|
2209
2234
|
// The Details panel: the same facts, grouped and said once.
|
|
2210
2235
|
const lastRunForAgent = liveRun ?? familyRuns.filter(run => run.role !== "reviewer").sort((a, b) => b.id - a.id)[0];
|
|
2211
2236
|
const work = [];
|
|
2212
|
-
|
|
2237
|
+
// A put-away task's one status is Archived (its banner says by whom and why).
|
|
2238
|
+
work.push({ label: "Status", parts: [data.archived != null ? "Archived" : data.assignment != null && assignmentOptions !== null ? assignmentCardOf(data.assignment, assignmentOptions).status.headline : status.label] });
|
|
2213
2239
|
if (data.repo !== null)
|
|
2214
2240
|
work.push({ label: "Project", parts: [projectName(data.repo)] });
|
|
2215
2241
|
if (data.base != null)
|
|
@@ -2320,6 +2346,7 @@ export function taskBodyParts(data) {
|
|
|
2320
2346
|
...(sectionIds.has("waits-for") ? [{ group: "work", label: "Dependencies", kind: "goto", tab: "plan", target: "waits-for" }] : []),
|
|
2321
2347
|
...(sectionIds.has("holds") ? [{ group: "work", label: "Holds", kind: "goto", tab: "plan", target: "holds" }] : []),
|
|
2322
2348
|
...(data.csrf !== "" ? [{ group: "work", label: "Task options", kind: "goto", tab: "plan", target: "task-diagnostics" }] : []),
|
|
2349
|
+
...(data.canPutAway === true && data.csrf !== "" && data.archived == null && !building ? [{ group: "work", label: "Put away", kind: "post", action: `${taskHref(task.id)}/archive`, fields: { return: taskHref(task.id) } }] : []),
|
|
2323
2350
|
{ group: "share", label: "Copy link", kind: "copy" },
|
|
2324
2351
|
...(facts.some(one => one.label === "Audit") ? [{ group: "share", label: "Evidence pack", kind: "link", href: `${taskHref(rootOf)}/evidence` }] : []),
|
|
2325
2352
|
...(data.repo !== null && data.scope !== null ? [{ group: "share", label: "Reuse as recipe", kind: "link", href: `/recipes/from-task?task=${encodeURIComponent(task.id)}` }] : []),
|
|
@@ -2336,10 +2363,12 @@ export function taskBodyParts(data) {
|
|
|
2336
2363
|
{ label: "Ask", href: taskChatHref(data.rootId ?? task.id), active: false },
|
|
2337
2364
|
],
|
|
2338
2365
|
version: data.versionLabel == null ? null : { label: data.versionLabel, current: { label: "Current work", href: taskHref(data.rootId ?? task.id) } },
|
|
2339
|
-
|
|
2366
|
+
archived: data.archived == null ? null : { words: archivedWords(data.archived), at: data.archived.at,
|
|
2367
|
+
restore: data.canPutAway === true && data.csrf !== "" ? { action: `${taskHref(task.id)}/restore`, returnTo: taskHref(task.id) } : null },
|
|
2368
|
+
status: statusCard === null || data.archived != null ? null : inPlaceChecks(failedCard ? { ...statusCard, action: null, status: { ...statusCard.status, sentence: data.failure?.line ?? statusCard.status.sentence } }
|
|
2340
2369
|
: progress !== null || record !== null ? { ...statusCard, action: statusCard.action?.href === record?.href ? null : statusCard.action, status: { ...statusCard.status, sentence: progress?.line ?? statusCard.status.sentence } }
|
|
2341
2370
|
: statusCard.action !== null && rebuild === null && bareSelfLink(statusCard.action.href) ? { ...statusCard, action: null } : statusCard),
|
|
2342
|
-
statusHtml: htmlString(statusHtml),
|
|
2371
|
+
statusHtml: data.archived != null ? "" : htmlString(statusHtml),
|
|
2343
2372
|
failure: failedCard && data.failure != null ? { line: data.failure.line, evidence: data.failure.evidence, suggestion: data.failure.suggestion, link: data.failure.link } : null,
|
|
2344
2373
|
retry,
|
|
2345
2374
|
runChecks,
|
|
@@ -2352,8 +2381,11 @@ export function taskBodyParts(data) {
|
|
|
2352
2381
|
...(data.csrf !== "" && (data.steerRecent?.length ?? 0) > 0 ? { steerRecent: data.steerRecent } : {}),
|
|
2353
2382
|
record,
|
|
2354
2383
|
earlier,
|
|
2355
|
-
approval: fragmentString(approvalHtml),
|
|
2384
|
+
approval: data.archived != null ? "" : fragmentString(approvalHtml),
|
|
2356
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,
|
|
2357
2389
|
more,
|
|
2358
2390
|
confirmStopped: data.csrf !== "" && data.assignment?.primaryAction?.code === "confirm-stopped" && data.assignment.primaryAction.target.runId !== null
|
|
2359
2391
|
? { action: `${taskHref(data.assignment.rootId)}/confirm-stopped`, run: data.assignment.primaryAction.target.runId, checked: needsCheck(data.assignment) } : null,
|
|
@@ -2477,29 +2509,6 @@ export function decisionOptionForms(decision, returnTo) {
|
|
|
2477
2509
|
: html `<details class="arm-danger"><summary>${option.label} — irreversible, tap to arm</summary>${inner}</details>`;
|
|
2478
2510
|
}), "\n");
|
|
2479
2511
|
}
|
|
2480
|
-
export function decisionPage(chrome, decision, taskId, artifacts, who, now, returnTo = null) {
|
|
2481
|
-
const options = decisionOptionForms(decision, returnTo);
|
|
2482
|
-
const answered = decision.supersededReason !== null
|
|
2483
|
-
? html `<p class="meta">${closedQuestionReason(decision.supersededReason)}</p>`
|
|
2484
|
-
: decision.state === "answered"
|
|
2485
|
-
? html `<div class="answered">Answered: <strong>${decision.choice ?? ""}</strong> by ${decision.answeredBy ?? ""}${decision.note === null ? "" : ` — ${decision.note}`}</div>`
|
|
2486
|
-
: "";
|
|
2487
|
-
const evidence = artifacts.length === 0
|
|
2488
|
-
? ""
|
|
2489
|
-
: joinHtml([
|
|
2490
|
-
html `<div class="evidence"><strong>Evidence</strong>`,
|
|
2491
|
-
joinHtml(artifacts.map(artifact => html `<a href="/d/${decision.id}/evidence/${artifact.id}">${evidenceWords(artifact.kind)}${artifact.truncated ? " (truncated)" : ""} · ${artifact.bytesStored} bytes</a>`), "\n"),
|
|
2492
|
-
html `</div>`,
|
|
2493
|
-
]);
|
|
2494
|
-
return screen(`decide · ${taskId}`, joinHtml([
|
|
2495
|
-
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>`,
|
|
2496
|
-
html `<div class="recap">${decision.recap}</div>`,
|
|
2497
|
-
html `<div class="question">${decision.question}</div>`,
|
|
2498
|
-
decision.state === "answered" ? answered : options,
|
|
2499
|
-
evidence,
|
|
2500
|
-
html `<p class="meta"><a href="${returnTo === null ? "/" : returnTo}">← ${returnTo === null ? "everything waiting" : "back to the task chat"}</a></p>`,
|
|
2501
|
-
], "\n"), { chrome });
|
|
2502
|
-
}
|
|
2503
2512
|
export function taskOf(store, decision) {
|
|
2504
2513
|
const run = store.getRun(decision.run);
|
|
2505
2514
|
return run === null ? "?" : store.externalIdFor(run.taskRef) ?? "?";
|
|
@@ -2527,3 +2536,34 @@ export function releaseCardHtml(taskId, release, canStop) {
|
|
|
2527
2536
|
html `</div>`,
|
|
2528
2537
|
], "\n");
|
|
2529
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,22 +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 });
|
|
135
|
+
// Put away old tasks (task-tidy.md): the preview reads only; putting away is an approver's step-up in a browser session.
|
|
136
|
+
get("tasks", "work.put-away", "/work/put-away", { limited: "collection", ...free, callers: ["cookie"], ...approver("Only an approver can put tasks away.", "/work") });
|
|
137
|
+
post("tasks", "work.put-away.apply", "/work/put-away", { scope: "step-up", limited: "collection", ...approver("Only an approver can put tasks away.", "/work") });
|
|
130
138
|
get("tasks", "next", "/next", { limited: "collection" });
|
|
131
|
-
get("pages", "morning", "/morning");
|
|
139
|
+
get("pages", "morning", "/morning", { limited: "collection", ...free }); // a redirect to / now
|
|
132
140
|
get("pages", "workbench", "/workbench", free);
|
|
133
141
|
get("tasks", "board", "/board", { limited: "collection" });
|
|
134
142
|
get("tasks", "review", "/review", { limited: "collection" });
|
|
135
143
|
get("settings", "spend", "/spend", { ...free, ...anyOperator("An instance operator sees spend.", "/") });
|
|
136
144
|
get("settings", "settings.spend", "/settings/spend", { ...free, ...anyOperator("An instance operator sets spending limits.", "/settings") });
|
|
137
145
|
get("pages", "ledger", "/ledger", { limited: "unscoped", ...free });
|
|
138
|
-
get("pages", "activity", "/activity");
|
|
139
|
-
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
|
|
140
148
|
get("settings", "mode", "/mode");
|
|
141
149
|
get("people", "people.page", "/people", { limited: "unscoped", ...free });
|
|
142
|
-
get("pages", "system", "/system");
|
|
150
|
+
get("pages", "system", "/system", free);
|
|
143
151
|
// Where every "Connect a builder" leads: read-only, open to a project-limited person (the handler shows only a visible project).
|
|
144
152
|
get("pages", "start-builder", "/start-builder", { limited: "unscoped", ...free });
|
|
145
153
|
get("tasks", "tasks", "/tasks", { limited: "collection", ...free }); // a redirect to /work now: never sent to the project opener first
|
|
@@ -150,12 +158,17 @@ get("pages", "agents", "/agents", { limited: "unscoped", ...free });
|
|
|
150
158
|
// Browser sessions only, like the transcript and the peek: no token (a lead's, a runner's, a remote one) reads live output.
|
|
151
159
|
get("pages", "agents.live", "/agents/live", { limited: "unscoped", callers: ["cookie"], ...free });
|
|
152
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.
|
|
153
164
|
get("pages", "fleet", "/fleet", free);
|
|
154
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 });
|
|
155
168
|
get("tasks", "task.page", `^\\/t\\/${TASK}$`, { project: "task", limited: "resource", ...free, sample: "/t/one" });
|
|
156
169
|
get("tasks", "task.evidence", `^\\/t\\/${TASK}\\/evidence$`, { project: "task", limited: "resource", ...free, sample: "/t/one/evidence" });
|
|
157
170
|
get("pages", "ledger.export", "/ledger/export", { limited: "unscoped", ...free });
|
|
158
|
-
get("tasks", "runs", "/runs", { limited: "collection" });
|
|
171
|
+
get("tasks", "runs", "/runs", { limited: "collection", ...free }); // a redirect to /agents now
|
|
159
172
|
get("pages", "menu", "/menu", { limited: "collection", ...free });
|
|
160
173
|
get("tasks", "run.page", `^\\/r\\/${ID}$`, { project: "run", limited: "resource", ...free, sample: "/r/1" });
|
|
161
174
|
get("tasks", "run.evidence", `^\\/r\\/${ID}\\/evidence\\/${ID}$`, { project: "run", limited: "resource", ...free, sample: "/r/1/evidence/2" });
|
|
@@ -215,6 +228,11 @@ get("settings", "settings.knowledge", "/settings/knowledge", { limited: "unscope
|
|
|
215
228
|
get("settings", "settings.learning", "/settings/learning", { limited: "unscoped", ...free });
|
|
216
229
|
get("settings", "settings.telegram", "/settings/telegram", { limited: "self", ...free, ...approver("An approver can pair their own phone.", "/settings") });
|
|
217
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 });
|
|
218
236
|
get("pages", "health", "/health", { ...free, ...anyOperator("An instance operator reads server health.", "/") });
|
|
219
237
|
get("pages", "metrics", "/metrics", { ...free, ...anyOperator("An instance operator reads metrics.", "/") });
|
|
220
238
|
get("chat", "push.key", "/push/key");
|
|
@@ -270,6 +288,11 @@ for (const one of ["linear-key", "hooks-address", "secrets"])
|
|
|
270
288
|
// "Runs on its own": signing raises authority (the owner's password, a step-up); revoking is one click for any approver.
|
|
271
289
|
post("flows", "flow.unattended", `^\\/flows\\/${FLOW}\\/unattended$`, { scope: "step-up", project: "flow", sample: "/flows/1/unattended", ...EDIT_FLOWS });
|
|
272
290
|
post("flows", "flow.unattended-off", `^\\/flows\\/${FLOW}\\/unattended-off$`, { project: "flow", sample: "/flows/1/unattended-off", ...EDIT_FLOWS });
|
|
291
|
+
// Browser use in a flow's script zones (flow-browser.ts): allowing it widens the sandbox, so a person's own step-up from
|
|
292
|
+
// their browser, carrying the approval digest they read; turning it off lowers authority, one click. Browser only: a
|
|
293
|
+
// lead's or remote run's token never reaches either (the CLI says the same).
|
|
294
|
+
post("flows", "flow.browser", `^\\/flows\\/${FLOW}\\/browser$`, { scope: "step-up", project: "flow", sample: "/flows/1/browser", ...EDIT_FLOWS });
|
|
295
|
+
post("flows", "flow.browser-off", `^\\/flows\\/${FLOW}\\/browser-off$`, { callers: ["cookie"], project: "flow", limited: "resource", sample: "/flows/1/browser-off", ...EDIT_FLOWS });
|
|
273
296
|
post("flows", "flow.triggers", `^\\/flows\\/${FLOW}\\/triggers$`, { project: "flow", limited: "resource", sample: "/flows/1/triggers", ...EDIT_FLOWS });
|
|
274
297
|
for (const one of ["move", "decide", "cancel", "comment", "assign", "watch"])
|
|
275
298
|
post("flows", `flow.card.${one}`, `^\\/flows\\/${FLOW}\\/cards\\/${FLOW}\\/${one}$`, { project: "flow", limited: "resource", sample: `/flows/1/cards/2/${one}`, ...EDIT_FLOWS });
|
|
@@ -408,6 +431,9 @@ post("tasks", "task.act.merge", `^\\/t\\/${TASK}\\/merge$`, { project: "task", l
|
|
|
408
431
|
post("tasks", "task.act.confirm-stopped", `^\\/t\\/${TASK}\\/confirm-stopped$`, { scope: "step-up", project: "task", limited: "resource", sample: "/t/one/confirm-stopped", ...approver("Only an approver can confirm a build stopped.") });
|
|
409
432
|
post("tasks", "task.act.stop-carrying", `^\\/t\\/${TASK}\\/stop-carrying$`, { project: "task", limited: "resource", sample: "/t/one/stop-carrying", ...approver("Only an approver can stop carrying a release.") });
|
|
410
433
|
post("tasks", "task.act.stop", `^\\/t\\/${TASK}\\/stop$`, { project: "task", limited: "resource", sample: "/t/one/stop" });
|
|
434
|
+
// Putting one task away and bringing it back (task-tidy.md): a person's act, so only a browser session.
|
|
435
|
+
post("tasks", "task.act.archive", `^\\/t\\/${TASK}\\/archive$`, { project: "task", limited: "resource", sample: "/t/one/archive", callers: ["cookie"], ...approver("Only an approver can put a task away.") });
|
|
436
|
+
post("tasks", "task.act.restore", `^\\/t\\/${TASK}\\/restore$`, { project: "task", limited: "resource", sample: "/t/one/restore", callers: ["cookie"], ...approver("Only an approver can restore a task.") });
|
|
411
437
|
post("tasks", "task.act.pause", `^\\/t\\/${TASK}\\/pause$`, { project: "task", limited: "resource", sample: "/t/one/pause" });
|
|
412
438
|
post("tasks", "task.act.resume-arm", `^\\/t\\/${TASK}\\/resume-arm$`, { project: "task", limited: "resource", sample: "/t/one/resume-arm" });
|
|
413
439
|
post("tasks", "task.act.resume", `^\\/t\\/${TASK}\\/resume$`, { scope: "step-up", project: "task", limited: "resource", sample: "/t/one/resume" });
|