toolroll 0.9.92 → 0.9.94
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 +87 -0
- package/dist/activity-line.d.ts +7 -4
- package/dist/activity-line.js +9 -7
- package/dist/agent-fence.d.ts +4 -0
- package/dist/agent-fence.js +4 -1
- package/dist/approval-authority.d.ts +7 -1
- package/dist/approval-authority.js +11 -2
- package/dist/approval-password.d.ts +31 -0
- package/dist/approval-password.js +26 -0
- package/dist/assignment-status.d.ts +1 -1
- package/dist/assignment-ui.js +3 -3
- package/dist/assignment.js +16 -3
- package/dist/boot-identity.d.ts +2 -1
- package/dist/boot-identity.js +6 -4
- package/dist/bounded-spawn.d.ts +57 -0
- package/dist/bounded-spawn.js +220 -0
- package/dist/browser/terminal.js +42 -42
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +26 -26
- package/dist/browser-crew.js +2 -2
- package/dist/browser-shell.d.ts +1 -1
- package/dist/browser-shell.js +2 -2
- package/dist/browser-workspace.d.ts +48 -7
- package/dist/browser-workspace.js +3 -3
- package/dist/builder-restart.d.ts +21 -0
- package/dist/builder-restart.js +50 -0
- package/dist/builder.d.ts +4 -0
- package/dist/builder.js +73 -25
- package/dist/chat-acceptance.js +1 -1
- package/dist/chat-actions.d.ts +12 -2
- package/dist/chat-actions.js +25 -2
- package/dist/chat-channel.js +11 -0
- package/dist/chat-decide.d.ts +7 -2
- package/dist/chat-decide.js +37 -9
- package/dist/chat-quiet.js +3 -3
- package/dist/chat-voice.d.ts +1 -1
- package/dist/chat-voice.js +2 -2
- package/dist/check-facts.d.ts +126 -0
- package/dist/check-facts.js +318 -0
- package/dist/check-failure.d.ts +22 -3
- package/dist/check-failure.js +57 -13
- package/dist/check-levels-ui.d.ts +4 -0
- package/dist/check-levels-ui.js +10 -1
- package/dist/check-load.d.ts +85 -0
- package/dist/check-load.js +147 -0
- package/dist/check-only-standing.d.ts +36 -0
- package/dist/check-only-standing.js +39 -0
- package/dist/check-only.d.ts +15 -0
- package/dist/check-only.js +99 -0
- package/dist/check-report.d.ts +7 -0
- package/dist/check-report.js +4 -3
- package/dist/check-stamp.d.ts +46 -0
- package/dist/check-stamp.js +93 -0
- package/dist/checkout-cleanup.d.ts +36 -5
- package/dist/checkout-cleanup.js +166 -14
- package/dist/cli-help.d.ts +4 -1
- package/dist/cli-help.js +47 -30
- package/dist/cli.js +16 -1
- package/dist/code-extract.d.ts +44 -0
- package/dist/code-extract.js +88 -0
- package/dist/code-map-acts.d.ts +24 -0
- package/dist/code-map-acts.js +53 -0
- package/dist/code-map-admission.d.ts +52 -0
- package/dist/code-map-admission.js +295 -0
- package/dist/code-map-build.d.ts +63 -0
- package/dist/code-map-build.js +415 -0
- package/dist/code-map-cli.d.ts +153 -0
- package/dist/code-map-cli.js +103 -0
- package/dist/code-map-main.d.ts +1 -0
- package/dist/code-map-main.js +48 -0
- package/dist/code-map-read.d.ts +106 -0
- package/dist/code-map-read.js +171 -0
- package/dist/code-map-report.d.ts +37 -0
- package/dist/code-map-report.js +95 -0
- package/dist/code-map-shape.d.ts +172 -0
- package/dist/code-map-shape.js +327 -0
- package/dist/code-map-store.d.ts +136 -0
- package/dist/code-map-store.js +233 -0
- package/dist/coding-provider.d.ts +2 -0
- package/dist/coding-provider.js +3 -2
- package/dist/coding-update.js +3 -2
- package/dist/coding-workspace.js +4 -4
- package/dist/console-chrome.js +2 -2
- package/dist/containment.d.ts +5 -0
- package/dist/containment.js +14 -1
- package/dist/contracts/chat-actions.d.ts +9 -0
- package/dist/contracts/chat-actions.js +2 -0
- package/dist/contracts/cli.d.ts +52 -0
- package/dist/contracts/cli.js +4 -0
- package/dist/contracts/console-api.d.ts +12 -5
- package/dist/contracts/console-api.js +12 -5
- package/dist/contracts/gateway-tools.d.ts +17 -0
- package/dist/contracts/gateway-tools.js +3 -1
- package/dist/contracts/lead-tools.d.ts +61 -0
- package/dist/contracts/lead-tools.js +10 -1
- package/dist/contracts/project-knowledge.d.ts +38 -5
- package/dist/contracts/project-knowledge.js +11 -5
- package/dist/contracts/project-memory.d.ts +3 -1
- package/dist/contracts/project-memory.js +3 -2
- package/dist/contracts/release-check.d.ts +25 -1
- package/dist/contracts/release-check.js +19 -0
- package/dist/contracts/storage.d.ts +14 -0
- package/dist/contracts/storage.js +3 -1
- package/dist/contracts/store-json.d.ts +2 -0
- package/dist/contracts/youiyoux-request.d.ts +1 -1
- package/dist/contracts/youiyoux-request.js +1 -1
- package/dist/desktop-access.js +6 -2
- package/dist/desktop-update.d.ts +2 -2
- package/dist/desktop-update.js +13 -9
- package/dist/dispatch.js +18 -15
- package/dist/dispose.js +8 -2
- package/dist/easy-start.js +2 -3
- package/dist/eval-hooks.d.ts +55 -0
- package/dist/eval-hooks.js +227 -4
- package/dist/evals.js +2 -1
- package/dist/fast-start.d.ts +136 -0
- package/dist/fast-start.js +431 -0
- package/dist/fast-terms.d.ts +9 -0
- package/dist/fast-terms.js +23 -0
- package/dist/fenced-check-coverage.d.ts +62 -0
- package/dist/fenced-check-coverage.js +54 -0
- package/dist/first-run.js +1 -1
- package/dist/flaky-ci.d.ts +31 -0
- package/dist/flaky-ci.js +149 -0
- package/dist/flaky-cli.d.ts +60 -0
- package/dist/flaky-cli.js +60 -0
- package/dist/flaky.d.ts +83 -0
- package/dist/flaky.js +264 -0
- package/dist/flow-card-task.js +3 -0
- package/dist/flow-triggers.d.ts +1 -1
- package/dist/flow-triggers.js +1 -1
- package/dist/github-import.d.ts +62 -0
- package/dist/github-import.js +146 -0
- package/dist/guides.js +15 -10
- package/dist/invoke.d.ts +5 -1
- package/dist/invoke.js +49 -14
- package/dist/job-object-helper.ps1 +10 -3
- package/dist/keymap.d.ts +41 -0
- package/dist/keymap.js +51 -0
- package/dist/knowledge-cli.js +1 -1
- package/dist/knowledge-ui.d.ts +21 -4
- package/dist/knowledge-ui.js +75 -7
- package/dist/laptop-background.d.ts +16 -0
- package/dist/laptop-background.js +108 -2
- package/dist/lead-context.js +1 -1
- package/dist/lead-contract.d.ts +1 -1
- package/dist/lead-contract.js +2 -2
- package/dist/lead-progress.js +3 -0
- package/dist/lead-status.d.ts +2 -0
- package/dist/lead-status.js +6 -2
- package/dist/lead-tools.js +51 -2
- package/dist/lead.js +1 -1
- package/dist/lesson-notes.d.ts +81 -0
- package/dist/lesson-notes.js +336 -0
- package/dist/limits-ui.js +1 -1
- package/dist/local-owner.d.ts +1 -1
- package/dist/local-owner.js +11 -0
- package/dist/machine-check.d.ts +16 -0
- package/dist/machine-check.js +26 -0
- package/dist/machine-identity.d.ts +75 -0
- package/dist/machine-identity.js +170 -0
- package/dist/mcp-core.js +11 -1
- package/dist/memory-cli.d.ts +300 -1
- package/dist/memory-cli.js +62 -9
- package/dist/memory-pass.d.ts +16 -0
- package/dist/memory-pass.js +67 -3
- package/dist/memory-tidy.d.ts +181 -0
- package/dist/memory-tidy.js +523 -0
- package/dist/memory-use.d.ts +35 -0
- package/dist/memory-use.js +109 -0
- package/dist/merge-checklist.d.ts +55 -0
- package/dist/merge-checklist.js +182 -0
- package/dist/needs-you.d.ts +32 -13
- package/dist/needs-you.js +108 -49
- package/dist/nightly-github.d.ts +2 -0
- package/dist/nightly-github.js +4 -4
- package/dist/operate-remote-arguments.js +6 -1
- package/dist/operate-remote.js +3 -1
- package/dist/operate.d.ts +6 -1
- package/dist/operate.js +540 -70
- package/dist/phase-routing.js +2 -2
- package/dist/plain-schema.d.ts +23 -0
- package/dist/plain-schema.js +94 -0
- package/dist/plain-words.d.ts +20 -0
- package/dist/plain-words.js +66 -0
- package/dist/planner.js +2 -1
- package/dist/process-liveness.d.ts +2 -0
- package/dist/process-liveness.js +61 -17
- package/dist/project-delete-ui.d.ts +7 -2
- package/dist/project-delete-ui.js +12 -1
- package/dist/project-delete.js +13 -2
- package/dist/project-knowledge.d.ts +18 -11
- package/dist/project-knowledge.js +39 -9
- package/dist/project-learning.d.ts +2 -1
- package/dist/project-learning.js +15 -45
- package/dist/project-memory.d.ts +5 -0
- package/dist/project-memory.js +22 -2
- package/dist/proof.js +30 -5
- package/dist/provider-auth.js +1 -1
- package/dist/publish.js +9 -1
- package/dist/pull-request-flow.js +5 -0
- package/dist/push.d.ts +26 -3
- package/dist/push.js +55 -15
- package/dist/release-adapters.d.ts +9 -0
- package/dist/release-adapters.js +87 -1
- package/dist/release-approval.d.ts +191 -0
- package/dist/release-approval.js +382 -0
- package/dist/release-auto.js +26 -17
- package/dist/release-card.d.ts +74 -6
- package/dist/release-card.js +179 -23
- package/dist/release-carry.d.ts +7 -2
- package/dist/release-carry.js +39 -9
- package/dist/release-cli.d.ts +4 -0
- package/dist/release-cli.js +38 -23
- package/dist/release-coverage.js +1 -1
- package/dist/release-next.d.ts +48 -0
- package/dist/release-next.js +161 -0
- package/dist/release.d.ts +50 -1
- package/dist/release.js +118 -11
- package/dist/remote-destination.d.ts +36 -0
- package/dist/remote-destination.js +217 -0
- package/dist/remote-main.d.ts +48 -0
- package/dist/remote-main.js +84 -5
- package/dist/repository-context.d.ts +3 -2
- package/dist/repository-context.js +112 -70
- package/dist/restart-certification.d.ts +1 -0
- package/dist/restart-certification.js +8 -6
- package/dist/result-acts.d.ts +29 -21
- package/dist/result-acts.js +36 -25
- package/dist/result-follow-ups.d.ts +2 -2
- package/dist/result-follow-ups.js +18 -4
- package/dist/result-review.d.ts +1 -1
- package/dist/route-change-check.d.ts +4 -0
- package/dist/route-change-check.js +13 -0
- package/dist/route-change.d.ts +22 -0
- package/dist/route-change.js +89 -0
- package/dist/route-hooks.js +3 -2
- package/dist/route-results.d.ts +6 -1
- package/dist/route-results.js +14 -5
- package/dist/run-closeout.d.ts +1 -1
- package/dist/run-closeout.js +7 -5
- package/dist/runner-process.js +3 -2
- package/dist/runner.js +2 -1
- package/dist/scope.d.ts +6 -1
- package/dist/scope.js +22 -9
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +24 -18
- package/dist/server/chat.js +14 -12
- package/dist/server/chrome.d.ts +1 -1
- package/dist/server/chrome.js +26 -35
- package/dist/server/console.css +23 -0
- package/dist/server/guards.js +4 -0
- package/dist/server/home.d.ts +6 -0
- package/dist/server/home.js +21 -13
- package/dist/server/http.js +1 -1
- package/dist/server/kit.js +1 -1
- package/dist/server/merge-ready-view.d.ts +10 -0
- package/dist/server/merge-ready-view.js +30 -0
- package/dist/server/pages-assets.d.ts +1 -0
- package/dist/server/pages-assets.js +8 -5
- package/dist/server/pages.js +103 -82
- package/dist/server/render-chat.js +2 -2
- package/dist/server/render-code-map.d.ts +30 -0
- package/dist/server/render-code-map.js +137 -0
- package/dist/server/render-flaky.d.ts +20 -0
- package/dist/server/render-flaky.js +59 -0
- package/dist/server/render-history.js +1 -1
- package/dist/server/render-new-task.d.ts +26 -1
- package/dist/server/render-new-task.js +30 -5
- package/dist/server/render-pages.d.ts +1 -175
- package/dist/server/render-pages.js +38 -313
- package/dist/server/render-projects.d.ts +2 -1
- package/dist/server/render-projects.js +9 -7
- package/dist/server/render-release.d.ts +19 -0
- package/dist/server/render-release.js +84 -0
- package/dist/server/render-results.d.ts +2 -2
- package/dist/server/render-results.js +71 -73
- package/dist/server/render-tasks.d.ts +13 -2
- package/dist/server/render-tasks.js +156 -139
- package/dist/server/retired-routes.d.ts +17 -0
- package/dist/server/retired-routes.js +53 -0
- package/dist/server/route-table.d.ts +4 -1
- package/dist/server/route-table.js +52 -13
- package/dist/server/settings.js +129 -22
- package/dist/server/task-views.js +61 -26
- package/dist/server/tasks.d.ts +17 -6
- package/dist/server/tasks.js +273 -338
- package/dist/server/welcome.js +1 -1
- package/dist/settings-groups-ui.js +1 -1
- package/dist/setup-guide.d.ts +2 -2
- package/dist/stop-proof.d.ts +67 -0
- package/dist/stop-proof.js +295 -0
- package/dist/storage-sweep-process.js +1 -1
- package/dist/storage-sweep.d.ts +5 -3
- package/dist/storage-sweep.js +30 -9
- package/dist/storage-ui.js +28 -17
- package/dist/storage.d.ts +27 -2
- package/dist/storage.js +39 -2
- package/dist/store.d.ts +148 -34
- package/dist/store.js +626 -177
- package/dist/surface.js +46 -5
- package/dist/sweep-quarantine.d.ts +1 -0
- package/dist/sweep-quarantine.js +6 -4
- package/dist/tamper-scan.d.ts +4 -0
- package/dist/tamper-scan.js +90 -0
- package/dist/task-aliases.d.ts +5 -0
- package/dist/task-aliases.js +9 -0
- package/dist/task-control.js +4 -1
- package/dist/task-outcome-cli.d.ts +2 -0
- package/dist/task-outcome-cli.js +36 -17
- package/dist/task-sizing.d.ts +5 -0
- package/dist/task-sizing.js +2 -0
- package/dist/task-source.d.ts +7 -0
- package/dist/task-source.js +14 -0
- package/dist/task-status.d.ts +9 -0
- package/dist/task-status.js +40 -49
- package/dist/teardown-git.d.ts +16 -0
- package/dist/teardown-git.js +63 -5
- package/dist/telegram-progress.js +5 -3
- package/dist/telegram-status.js +6 -4
- package/dist/test-infra.d.ts +8 -0
- package/dist/test-infra.js +18 -0
- package/dist/tidy.js +1 -1
- package/dist/toolroll-update-ui.js +2 -2
- package/dist/toolroll-update.js +18 -5
- package/dist/tui-contract.d.ts +24 -0
- package/dist/tui-source.js +67 -8
- package/dist/tui.js +51 -51
- package/dist/vanished-retry.d.ts +30 -0
- package/dist/vanished-retry.js +174 -0
- package/dist/work-index.d.ts +2 -0
- package/dist/work-index.js +82 -70
- package/dist/work-summary.d.ts +2 -2
- package/dist/work-summary.js +2 -0
- package/dist/workspace-ui.d.ts +4 -2
- package/dist/workspace-ui.js +28 -22
- package/dist/worktree.d.ts +12 -3
- package/dist/worktree.js +68 -13
- package/package.json +1 -1
- package/dist/server/render-next.d.ts +0 -63
- package/dist/server/render-next.js +0 -109
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Retired addresses (docs/plans/plain-toolroll.md P1.4): Home (`/`) and Tasks (`/tasks`) are the pages; every old page
|
|
2
|
+
* answers with a permanent redirect to its place there, for good, so a bookmark, an old push or an installed phone's old
|
|
3
|
+
* service worker still lands right. A redirect keeps only the query keys its target knows, and decides nothing: the
|
|
4
|
+
* target admits the person and filters what they may see, exactly as if they had opened it. */
|
|
5
|
+
import type { ServerResponse } from "node:http";
|
|
6
|
+
/** The Tasks page's own address for a view, a project and the other keys it knows. */
|
|
7
|
+
export declare function tasksHref(over?: {
|
|
8
|
+
view?: string | null;
|
|
9
|
+
project?: string | null;
|
|
10
|
+
scope?: "all" | null;
|
|
11
|
+
state?: string | null;
|
|
12
|
+
cursor?: string | null;
|
|
13
|
+
}): string;
|
|
14
|
+
/** Each retired GET row (route-table.ts `retired`), and where it now lives. */
|
|
15
|
+
export declare const RETIRED_ROUTES: Readonly<Record<string, (url: URL) => string>>;
|
|
16
|
+
/** A retired GET's answer: 301 to where it lives now. Permanent: the old address never comes back. */
|
|
17
|
+
export declare function retiredRedirect(id: string, url: URL, response: ServerResponse): void;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { SAFETY } from "./http.js";
|
|
2
|
+
/** The Tasks page's own address for a view, a project and the other keys it knows. */
|
|
3
|
+
export function tasksHref(over = {}) {
|
|
4
|
+
const q = new URLSearchParams();
|
|
5
|
+
if (over.scope === "all")
|
|
6
|
+
q.set("scope", "all");
|
|
7
|
+
else if (over.project != null)
|
|
8
|
+
q.set("project", over.project);
|
|
9
|
+
if (over.view != null && over.view !== "all")
|
|
10
|
+
q.set("view", over.view);
|
|
11
|
+
if (over.state != null)
|
|
12
|
+
q.set("state", over.state);
|
|
13
|
+
if (over.cursor != null)
|
|
14
|
+
q.set("cursor", over.cursor);
|
|
15
|
+
return `/tasks${q.size === 0 ? "" : `?${q}`}`;
|
|
16
|
+
}
|
|
17
|
+
/** The one project a link names (one `project` key, never two), or null. */
|
|
18
|
+
const projectOf = (url) => url.searchParams.getAll("project").length === 1 ? url.searchParams.get("project") : null;
|
|
19
|
+
/** The Tasks list views an old link may name, as /tasks names them. */
|
|
20
|
+
const LIST_VIEWS = { all: "all", "needs-you": "needs-you", running: "running", queued: "queued", completed: "completed", done: "completed", archived: "archived", ready: "ready" };
|
|
21
|
+
/** Each retired GET row (route-table.ts `retired`), and where it now lives. */
|
|
22
|
+
export const RETIRED_ROUTES = {
|
|
23
|
+
// The old Tasks list: its view, project, stored-state filter and page; layout=board is the Board view; all=1 every project.
|
|
24
|
+
work: url => {
|
|
25
|
+
const board = url.searchParams.get("layout") === "board";
|
|
26
|
+
const view = board ? "board" : LIST_VIEWS[url.searchParams.get("view") ?? ""] ?? null;
|
|
27
|
+
const all = url.searchParams.get("all") === "1";
|
|
28
|
+
return tasksHref({ view, scope: all ? "all" : null, project: all ? null : projectOf(url), state: board ? null : url.searchParams.get("state"), cursor: board ? null : url.searchParams.get("cursor") });
|
|
29
|
+
},
|
|
30
|
+
// The old inbox and its triage flow: Home holds what needs you.
|
|
31
|
+
inbox: url => { const project = projectOf(url); return project === null ? "/" : `/?project=${encodeURIComponent(project)}`; },
|
|
32
|
+
next: url => { const project = projectOf(url); return project === null ? "/" : `/?project=${encodeURIComponent(project)}`; },
|
|
33
|
+
// The portfolio: Tasks across every project.
|
|
34
|
+
workbench: () => tasksHref({ scope: "all" }),
|
|
35
|
+
// The old board: the Board view, or its order view.
|
|
36
|
+
board: url => url.searchParams.get("view") === "order" ? tasksHref({ view: "order", project: projectOf(url) })
|
|
37
|
+
: url.searchParams.get("scope") === "all" ? tasksHref({ view: "board", scope: "all" }) : tasksHref({ view: "board", project: projectOf(url) }),
|
|
38
|
+
queue: url => tasksHref({ view: "order", project: projectOf(url) }),
|
|
39
|
+
done: url => tasksHref({ view: "done", project: projectOf(url) }),
|
|
40
|
+
// The old review list (a result keeps its own page: /review?result=…).
|
|
41
|
+
review: url => tasksHref({ view: "ready", project: projectOf(url) }),
|
|
42
|
+
// Archive old tasks' preview moved beside Tasks; its POST keeps /work/put-away exactly.
|
|
43
|
+
"work.put-away": url => { const days = url.searchParams.get("days"); return days !== null && /^[0-9]{1,3}$/.test(days) ? `/tasks/archive?days=${days}` : "/tasks/archive"; },
|
|
44
|
+
};
|
|
45
|
+
/** A retired GET's answer: 301 to where it lives now. Permanent: the old address never comes back. */
|
|
46
|
+
export function retiredRedirect(id, url, response) {
|
|
47
|
+
const to = RETIRED_ROUTES[id];
|
|
48
|
+
if (to === undefined)
|
|
49
|
+
throw new Error(`no retired route ${id}`);
|
|
50
|
+
response.setHeader("Location", to(url));
|
|
51
|
+
response.writeHead(301, { ...SAFETY });
|
|
52
|
+
response.end();
|
|
53
|
+
}
|
|
@@ -22,7 +22,7 @@ export type Caller = "anonymous" | "service" | "cookie" | "bearer" | "oauth" | "
|
|
|
22
22
|
/** What the caller must hold: nothing, read, act (an approver or an `act` token), or a fresh password/provider check on top. */
|
|
23
23
|
export type RouteScope = "none" | "read" | "act" | "step-up";
|
|
24
24
|
/** Where the request's project comes from. */
|
|
25
|
-
export type ProjectResolver = "none" | "session" | "form" | "form-path" | "task" | "run" | "decision" | "incident" | "flow" | "proposal" | "conversation" | "coding" | "adapter";
|
|
25
|
+
export type ProjectResolver = "none" | "session" | "form" | "form-path" | "task" | "task-ref" | "run" | "decision" | "incident" | "flow" | "proposal" | "conversation" | "coding" | "adapter";
|
|
26
26
|
/**
|
|
27
27
|
* What a project-limited account may do here:
|
|
28
28
|
* - `deny`: instance access only.
|
|
@@ -94,6 +94,9 @@ export interface RouteDeclaration {
|
|
|
94
94
|
*/
|
|
95
95
|
readonly local: "allow" | "deny";
|
|
96
96
|
readonly localAct: LocalOwnerAct | null;
|
|
97
|
+
/** A retired page (docs/plans/plain-toolroll.md P1.4): a permanent GET redirect to where it lives now
|
|
98
|
+
* (retired-routes.ts), admitted exactly as that target is, and no page handler of its own. */
|
|
99
|
+
readonly retired?: true;
|
|
97
100
|
}
|
|
98
101
|
/** Every edge row, decided: allow where a local owner's cookie may reach it, deny where its adapter refuses one (or no
|
|
99
102
|
* cookie is ever read there). A new edge row fails the table's load until it is listed here. */
|
|
@@ -45,6 +45,7 @@ function row(stage, domain, id, method, pattern, options = {}) {
|
|
|
45
45
|
...(options.roleRefusal === undefined ? {} : { roleRefusal: options.roleRefusal }),
|
|
46
46
|
...(options.bodyCap === undefined ? {} : { bodyCap: options.bodyCap }),
|
|
47
47
|
freshness: options.freshness ?? "never",
|
|
48
|
+
...(options.retired === true ? { retired: true } : {}),
|
|
48
49
|
...localMarkOf(stage, method, id),
|
|
49
50
|
});
|
|
50
51
|
}
|
|
@@ -54,6 +55,7 @@ function row(stage, domain, id, method, pattern, options = {}) {
|
|
|
54
55
|
/** The console POST rows a local owner may use, and the act each is. Everything else is refused. */
|
|
55
56
|
const LOCAL_POSTS = Object.freeze({
|
|
56
57
|
"tasks.add": "file-task",
|
|
58
|
+
"tasks.fast": "build-now",
|
|
57
59
|
"chat.file": "file-task",
|
|
58
60
|
"chat.send": "lead-send",
|
|
59
61
|
"chat.queued-remove": "lead-send",
|
|
@@ -66,6 +68,7 @@ const LOCAL_POSTS = Object.freeze({
|
|
|
66
68
|
"chat.proposal.dismiss": "lead-send",
|
|
67
69
|
"task.act.steer": "lead-send",
|
|
68
70
|
"task.act.approve": "approve-plan",
|
|
71
|
+
"task.act.keep-grown": "approve-plan",
|
|
69
72
|
"decision.answer": "answer-question",
|
|
70
73
|
"task.act.reply": "answer-question",
|
|
71
74
|
"task.act.stop": "stop-run",
|
|
@@ -91,11 +94,16 @@ const LOCAL_POSTS = Object.freeze({
|
|
|
91
94
|
"settings.lead-on": "choose-default-agent",
|
|
92
95
|
// Choosing which open project the pages show: a view filter, never authorization.
|
|
93
96
|
"projects.select": "read",
|
|
97
|
+
// On or off for one project: it moves no money, permission or approval (code-map-acts.ts re-checks this exact act).
|
|
98
|
+
"settings.code-map": "code-map-switch",
|
|
94
99
|
// Results routing: lowering only.
|
|
95
100
|
"settings.models-turn-off": "routing-turn-off",
|
|
96
101
|
"settings.models-not-now": "dismiss-suggestion",
|
|
97
102
|
"settings.models-suggest-off": "dismiss-suggestion",
|
|
98
103
|
"settings.models-stop": "trial-stop",
|
|
104
|
+
// Learning loops (D3): a person at the laptop decides lesson notes and clean-ups of project memory.
|
|
105
|
+
"settings.knowledge-note": "memory-review",
|
|
106
|
+
"settings.knowledge-tidy": "memory-review",
|
|
99
107
|
"agents.seen": "mark-seen",
|
|
100
108
|
"agents.watch": "watch-agent",
|
|
101
109
|
});
|
|
@@ -245,30 +253,43 @@ get("pages", "projects.page", "/projects", { limited: "unscoped", ...free });
|
|
|
245
253
|
// project this account can see and never switches the session's project; its Open project button posts to
|
|
246
254
|
// /projects/select. The handler resolves the name among the account's visible projects; anything else is not found.
|
|
247
255
|
get("pages", "projects.project", "^\\/projects\\/[^/]+$", { limited: "unscoped", ...free, sample: "/projects/payments-api" });
|
|
256
|
+
// One project's code map (docs/plans/code-map.md §6): reads only. The handler resolves the name among the account's
|
|
257
|
+
// visible projects, and looks areas and files up exactly in the stored version.
|
|
258
|
+
get("pages", "projects.map", "^\\/projects\\/[^/]+\\/map$", { limited: "unscoped", ...free, sample: "/projects/payments-api/map" });
|
|
259
|
+
// A project's flaky tests list (docs/plans/learning-loops.md): reads only, found as the project's page finds its project.
|
|
260
|
+
// Its one action, "Make a task to fix it", posts the ordinary /tasks/add form.
|
|
261
|
+
get("pages", "projects.flaky", "^\\/projects\\/[^/]+\\/flaky$", { limited: "unscoped", ...free, sample: "/projects/payments-api/flaky" });
|
|
248
262
|
get("pages", "home", "/", { limited: "collection", ...free });
|
|
249
263
|
get("pages", "search", "/search", { limited: "collection", ...free });
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
get("tasks", "work
|
|
254
|
-
|
|
255
|
-
|
|
264
|
+
// Retired pages (docs/plans/plain-toolroll.md P1.4): permanent redirects to Home and Tasks, admitted as their target is.
|
|
265
|
+
const RETIRED = { limited: "collection", ...free, retired: true };
|
|
266
|
+
get("tasks", "inbox", "/inbox", RETIRED);
|
|
267
|
+
get("tasks", "work", "/work", RETIRED);
|
|
268
|
+
// Archive old tasks (task-tidy.md): the preview reads only, at /tasks/archive; archiving is an approver's step-up in a
|
|
269
|
+
// browser session, and keeps its POST /work/put-away exactly.
|
|
270
|
+
get("tasks", "work.put-away", "/work/put-away", { ...RETIRED, callers: ["cookie"], ...approver("Only an approver can archive tasks.", "/tasks") });
|
|
271
|
+
get("tasks", "tasks.archive", "/tasks/archive", { limited: "collection", ...free, callers: ["cookie"], ...approver("Only an approver can archive tasks.", "/tasks") });
|
|
272
|
+
post("tasks", "work.put-away.apply", "/work/put-away", { scope: "step-up", limited: "collection", ...approver("Only an approver can archive tasks.", "/tasks") });
|
|
273
|
+
get("tasks", "next", "/next", RETIRED);
|
|
256
274
|
get("pages", "morning", "/morning", { limited: "collection", ...free }); // a redirect to / now
|
|
257
|
-
get("pages", "workbench", "/workbench",
|
|
258
|
-
get("tasks", "board", "/board",
|
|
259
|
-
|
|
275
|
+
get("pages", "workbench", "/workbench", RETIRED);
|
|
276
|
+
get("tasks", "board", "/board", RETIRED);
|
|
277
|
+
// A result keeps its own page here (/review?result=…); the bare list is retired to Tasks' Ready view.
|
|
278
|
+
get("tasks", "review", "/review", { limited: "collection", ...free });
|
|
279
|
+
// A push's task link (P1.4): /n/<the task's stored number>, resolved to its page under the viewer's own access.
|
|
280
|
+
get("tasks", "n.task", "^\\/n\\/[1-9][0-9]{0,14}$", { limited: "unscoped", ...free, project: "none", sample: "/n/1" });
|
|
260
281
|
get("settings", "spend", "/spend", { ...free, ...anyOperator("An instance operator sees spend.", "/") });
|
|
261
282
|
get("settings", "settings.spend", "/settings/spend", { ...free, ...anyOperator("An instance operator sets spending limits.", "/settings") });
|
|
262
283
|
get("pages", "ledger", "/ledger", { limited: "unscoped", ...free });
|
|
263
284
|
get("pages", "activity", "/activity", { limited: "collection", ...free }); // a redirect to / now (Home has the summary)
|
|
264
|
-
get("tasks", "done", "/done",
|
|
285
|
+
get("tasks", "done", "/done", RETIRED);
|
|
265
286
|
get("settings", "mode", "/mode");
|
|
266
287
|
get("people", "people.page", "/people", { limited: "unscoped", ...free });
|
|
267
288
|
get("pages", "system", "/system", free);
|
|
268
289
|
// Where every "Connect a builder" leads: read-only, open to a project-limited person (the handler shows only a visible project).
|
|
269
290
|
get("pages", "start-builder", "/start-builder", { limited: "unscoped", ...free });
|
|
270
|
-
get("tasks", "tasks", "/tasks", { limited: "collection", ...free });
|
|
271
|
-
get("tasks", "queue", "/queue");
|
|
291
|
+
get("tasks", "tasks", "/tasks", { limited: "collection", ...free });
|
|
292
|
+
get("tasks", "queue", "/queue", RETIRED);
|
|
272
293
|
get("pages", "peek", "/peek", free);
|
|
273
294
|
get("pages", "agents", "/agents", { limited: "unscoped", ...free });
|
|
274
295
|
// The Agents view's Live grid (docs/plans/agents-view.md §3): read-only, every request filtered to what the viewer may see.
|
|
@@ -286,6 +307,10 @@ get("tasks", "welcome", "/welcome", { limited: "collection", ...free });
|
|
|
286
307
|
post("tasks", "welcome.sample", "/welcome/sample", { callers: ["cookie"], project: "none", ...approver("An approver adds projects.", "/welcome") });
|
|
287
308
|
get("tasks", "task.page", `^\\/t\\/${TASK}$`, { project: "task", limited: "resource", ...free, sample: "/t/one" });
|
|
288
309
|
get("tasks", "task.evidence", `^\\/t\\/${TASK}\\/evidence$`, { project: "task", limited: "resource", ...free, sample: "/t/one/evidence" });
|
|
310
|
+
// One-tap release approval (trusted-release-reports.md A6/A7): the sheet, and the push's opaque locator, which only
|
|
311
|
+
// redirects to it once the plane's own state says the task ref is a release in the person's projects.
|
|
312
|
+
get("tasks", "task.release", `^\\/t\\/${TASK}\\/release$`, { project: "task", limited: "resource", ...free, sample: "/t/one/release" });
|
|
313
|
+
get("tasks", "release.locate", `^\\/approve\\/${ID}$`, { project: "task-ref", limited: "resource", ...free, sample: "/approve/1" });
|
|
289
314
|
get("pages", "ledger.export", "/ledger/export", { limited: "unscoped", ...free });
|
|
290
315
|
get("tasks", "runs", "/runs", { limited: "collection", ...free }); // a redirect to /agents now
|
|
291
316
|
get("pages", "menu", "/menu", { limited: "collection", ...free });
|
|
@@ -496,9 +521,13 @@ for (const one of ["accept", "keep", "suggest-on", "try"])
|
|
|
496
521
|
for (const one of ["turn-off", "not-now", "suggest-off", "stop"])
|
|
497
522
|
post("settings", `settings.models-${one}`, `/settings/models/${one}`, operator("An instance operator changes which models run.", "/settings/models"));
|
|
498
523
|
// Any other models action answers the models area's own refusal.
|
|
524
|
+
post("settings", "settings.code-map", "/settings/project/code-map", { project: "form", ...approver("An approver turns the code map on or off.", "/settings/project") });
|
|
525
|
+
post("settings", "settings.code-map-holdout", "/settings/project/code-map/holdout", { project: "form", ...approver("A person ends the holdout.", "/settings/project") });
|
|
499
526
|
post("settings", "settings.models-act", "^\\/settings\\/models\\/.*$", { sample: "/settings/models/unknown", ...MODELS });
|
|
500
527
|
post("settings", "settings.knowledge-refresh", "/settings/knowledge/refresh", { project: "form", limited: "unscoped", ...approver("Sign in as an approver to refresh project context.", "/settings/knowledge") });
|
|
501
528
|
post("settings", "settings.knowledge-proposal", "/settings/knowledge/proposal", { project: "form", ...approver("Sign in as an approver to decide memory proposals.", "/settings/knowledge") });
|
|
529
|
+
post("settings", "settings.knowledge-note", "/settings/knowledge/note", { project: "form", ...approver("Sign in as an approver to decide lesson notes.", "/settings/knowledge") });
|
|
530
|
+
post("settings", "settings.knowledge-tidy", "/settings/knowledge/tidy", { project: "form", ...approver("Sign in as an approver to clean up project memory.", "/settings/knowledge") });
|
|
502
531
|
post("settings", "settings.knowledge-decision", "/settings/knowledge/decision", { project: "form", ...approver("Sign in as an approver to change project decisions.", "/settings/knowledge") });
|
|
503
532
|
post("settings", "settings.knowledge-change", "/settings/knowledge/change", { project: "form", limited: "unscoped", ...approver("Sign in as an approver to change project knowledge.", "/settings/knowledge") });
|
|
504
533
|
post("settings", "settings.learning-change", "/settings/learning/change", { project: "form", limited: "unscoped" });
|
|
@@ -521,9 +550,12 @@ for (const one of ["messaging", "permission-default", "quality-default", "notifi
|
|
|
521
550
|
}
|
|
522
551
|
post("pages", "projects.github-list", "/projects/github", { callers: ["cookie"], ...anyApprover("your login can watch — adding projects is an approver's act") });
|
|
523
552
|
post("pages", "projects.select", "/projects/select", { project: "form-path", limited: "unscoped", viewer: true });
|
|
553
|
+
post("pages", "projects.map-refresh", "^\\/projects\\/[^/]+\\/map\\/refresh$", { project: "form", sample: "/projects/payments-api/map/refresh", ...approver("An approver asks for a check of main.", "/projects") });
|
|
524
554
|
post("pages", "projects.remove", "/projects/remove", { project: "form", ...operator("An instance operator removes projects.", "/settings/project") });
|
|
525
555
|
post("pages", "projects.open", "/projects/open", { project: "form-path" });
|
|
526
556
|
post("tasks", "tasks.add", "/tasks/add", { project: "form", limited: "collection", bodyCap: LARGE_FORM });
|
|
557
|
+
// Build (fast-start.ts): a person's own browser session only. No token, chat, agent or webhook reaches it.
|
|
558
|
+
post("tasks", "tasks.fast", "/tasks/fast", { callers: ["cookie"], project: "none", limited: "collection", ...approver("An approver starts builds.", "/tasks/new") });
|
|
527
559
|
post("tasks", "queue.move", "/queue/move");
|
|
528
560
|
post("tasks", "queue.note", "/queue/note");
|
|
529
561
|
post("pages", "fleet.register", "/fleet/runner/register", { scope: "step-up" });
|
|
@@ -537,6 +569,9 @@ post("people", "people.revoke", "/people/revoke", { scope: "step-up" });
|
|
|
537
569
|
post("pages", "fleet.retire", "/fleet/runner/retire", { scope: "step-up" });
|
|
538
570
|
// Answering a parked decision needs act access and irreversible-choice confirmation, not password step-up.
|
|
539
571
|
post("tasks", "decision.answer", `^\\/d\\/${ID}\\/answer$`, { project: "decision", limited: "resource", scope: "act", sample: "/d/1/answer" });
|
|
572
|
+
// A held automatic release goes on: a person, with their password typed now (release-auto.ts allowAutoPublish).
|
|
573
|
+
post("tasks", "task.release.go-on", `^\\/t\\/${TASK}\\/release\\/go-on$`, { project: "task", limited: "resource", scope: "step-up", sample: "/t/one/release/go-on", ...approver("An approver lets a release go on.", "/tasks") });
|
|
574
|
+
post("tasks", "task.act.keep-grown", `^\\/t\\/${TASK}\\/keep-grown$`, { project: "task", limited: "resource", sample: "/t/one/keep-grown", callers: ["cookie"], ...approver("An approver keeps a change.", "/tasks") });
|
|
540
575
|
post("tasks", "task.act.hold", `^\\/t\\/${TASK}\\/hold$`, { project: "task", limited: "resource", sample: "/t/one/hold" });
|
|
541
576
|
post("tasks", "task.act.unhold", `^\\/t\\/${TASK}\\/unhold$`, { project: "task", limited: "resource", sample: "/t/one/unhold" });
|
|
542
577
|
post("tasks", "task.act.requeue", `^\\/t\\/${TASK}\\/requeue$`, { project: "task", limited: "resource", sample: "/t/one/requeue" });
|
|
@@ -556,7 +591,9 @@ post("tasks", "task.act.route", `^\\/t\\/${TASK}\\/route$`, { project: "task", l
|
|
|
556
591
|
post("tasks", "task.act.retry-review", `^\\/t\\/${TASK}\\/retry-review$`, { project: "task", limited: "resource", sample: "/t/one/retry-review" });
|
|
557
592
|
post("tasks", "task.act.complete", `^\\/t\\/${TASK}\\/complete$`, { project: "task", limited: "resource", sample: "/t/one/complete", ...approver("Only an approver can mark a result complete.") });
|
|
558
593
|
post("tasks", "task.act.merge", `^\\/t\\/${TASK}\\/merge$`, { project: "task", limited: "resource", sample: "/t/one/merge", ...approver("Only an approver can merge.") });
|
|
559
|
-
|
|
594
|
+
// Check again: Toolroll looks at this computer's processes for a finished build once more (stop-proof.ts). Nobody
|
|
595
|
+
// vouches that a build stopped, so no password: it only asks for the look the worker makes on its own anyway.
|
|
596
|
+
post("tasks", "task.act.recheck-stopped", `^\\/t\\/${TASK}\\/recheck-stopped$`, { project: "task", limited: "resource", sample: "/t/one/recheck-stopped", ...approver("Only an approver can have Toolroll check a build again.") });
|
|
560
597
|
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.") });
|
|
561
598
|
post("tasks", "task.act.stop", `^\\/t\\/${TASK}\\/stop$`, { project: "task", limited: "resource", sample: "/t/one/stop" });
|
|
562
599
|
// Putting one task away and bringing it back (task-tidy.md): a person's act, so only a browser session.
|
|
@@ -642,6 +679,8 @@ export function assertRouteTable(routes = ROUTES) {
|
|
|
642
679
|
throw new Error(`route table: ${route.id} role and its refusal go together`);
|
|
643
680
|
if (route.role !== "any" && route.stage !== "console")
|
|
644
681
|
throw new Error(`route table: ${route.id} edge routes prove roles in their protocol`);
|
|
682
|
+
if (route.retired === true && (route.method !== "GET" || route.stage !== "console"))
|
|
683
|
+
throw new Error(`route table: ${route.id} a retired page is a console GET`);
|
|
645
684
|
const test = new RegExp(route.pattern);
|
|
646
685
|
if (!test.test(route.sample))
|
|
647
686
|
throw new Error(`route table: ${route.id} does not match its sample ${route.sample}`);
|
package/dist/server/settings.js
CHANGED
|
@@ -7,6 +7,9 @@ import { finishGoogleConsent, GOOGLE_CALLBACK } from "../google-mail.js";
|
|
|
7
7
|
import { kitInstalled, kitOf } from "../kits.js";
|
|
8
8
|
import { CONNECT_CALLBACK, connectionsOf, finishConnect, localConnectOf, oneClickOf } from "../mcp-connect.js";
|
|
9
9
|
import { learningView } from "../project-learning.js";
|
|
10
|
+
import { memoryUseOf } from "../memory-use.js";
|
|
11
|
+
import { afterwardsWords, lessonNoteOf, noteAfterwards } from "../lesson-notes.js";
|
|
12
|
+
import { decideTidy, disabledLessons, listTidy, proposeTidy, restoreDecision, restoreLesson, restoreReference, undoableMerges, unmergeDecisions } from "../memory-tidy.js";
|
|
10
13
|
import { skillTestResult } from "../project-skills.js";
|
|
11
14
|
import { discoverTools, projectToolsOf } from "../project-tools.js";
|
|
12
15
|
import { lookProjectMcp } from "../folder-look.js";
|
|
@@ -48,6 +51,7 @@ import { releaseApprovalSetting, setReleaseApproval } from "../release-auto.js";
|
|
|
48
51
|
import { backupHtml } from "../backup-ui.js";
|
|
49
52
|
import { BACKUP_EVERY_HOURS, backupFolderOf, backupNow, checkBackupFolder, defaultBackupFolder, MAX_KEEP } from "../backup.js";
|
|
50
53
|
import { projectBatchChecks, setProjectBatchChecks } from '../batch-policy.js';
|
|
54
|
+
import { fastStartOn, setFastStart } from '../fast-start.js';
|
|
51
55
|
import { ALL_PROJECTS as CHAT_APPROVAL_ALL, chatApprovalWords, setChatApproval } from "../chat-approval.js";
|
|
52
56
|
import { ChatState } from "../chat-delivery-state.js";
|
|
53
57
|
import { checkSettingsHtml } from '../check-levels-ui.js';
|
|
@@ -67,13 +71,13 @@ import { disconnectGoogle, googleConnected, googleConsent, readGoogleMail, saveG
|
|
|
67
71
|
import { integrationsHtml } from "../integrations-ui.js";
|
|
68
72
|
import { setYouiyouxIntake } from "../youiyoux-intake.js";
|
|
69
73
|
import { clearProviderKey, keyStatus, plausibleKey, PROVIDER_KEY_ENV, readAuthMode, readProviderKey, saveProviderKey, setAuthMode, SUBSCRIPTION_CAPABLE, verdictWords, verifyProviderKey } from "../keys.js";
|
|
70
|
-
import { decisionsHtml, framedSection, knowledgeHtml, MEMORY_INTRO, memorySearchHtml, proposalsHtml } from "../knowledge-ui.js";
|
|
74
|
+
import { cleanUpHtml, decisionsHtml, framedSection, knowledgeHtml, MEMORY_INTRO, memorySearchHtml, proposalsHtml, referenceRestoreHtml, restoreHtml } from "../knowledge-ui.js";
|
|
71
75
|
import { aboutYouOf, checkAboutYou, saveAboutYou } from "../lead-about.js";
|
|
72
76
|
import { cancelCommitment, conditionWords, openCommitments } from '../lead-commitments.js';
|
|
73
77
|
import { checkLeadIdentity, leadIdentityOf } from "../lead-identity.js";
|
|
74
78
|
import { mailboxAccess, readThroughImap } from "../mailbox.js";
|
|
75
79
|
import { startConnect } from "../mcp-connect.js";
|
|
76
|
-
import { decideProposal, listProposals, memoryStatus } from "../memory-pass.js";
|
|
80
|
+
import { decideProposal, defaultTidyAnalyzer, listProposals, memoryStatus } from "../memory-pass.js";
|
|
77
81
|
import { checkModels, isNewModel, modelOptions, modelWords, RUNTIME_TOOLS, runtimeStates, seenModels, setWatch, updateRuntime, watchState } from "../model-catalog.js";
|
|
78
82
|
import { modelsHtml, modelsScript } from "../models-ui.js";
|
|
79
83
|
import { acceptSuggestion, dismissSuggestion, grantWords, keepRule, modelName, ruleWords, rulesOf, setSuggestModels, startTrial, stopTrial, suggestionsFor, suggestionWords, suggestModelsOn, trialOffersFor, trialOfferWords, trialsOf, trialWords, turnOffRule } from "../route-rules.js";
|
|
@@ -93,6 +97,7 @@ import { isCheckAccess, signInsUnavailableWords, widerThan } from "../check-acce
|
|
|
93
97
|
import { LOCAL_MODE_CONFINES, localModeOn } from "../local-owner.js";
|
|
94
98
|
import { parseMachLookup, parseReadDeny } from "../project-sandbox.js";
|
|
95
99
|
import { projectDeleteConfirmHtml, projectSettingsHtml } from "../project-delete-ui.js";
|
|
100
|
+
import { endHoldout, setCodeMapSwitch } from "../code-map-acts.js";
|
|
96
101
|
import { deleteProject, holdingsWords } from "../project-delete.js";
|
|
97
102
|
import { applySavedKnowledge, changeKnowledge, knowledgeVersion, knowledgeView } from "../project-knowledge.js";
|
|
98
103
|
import { changeLearning } from "../project-learning.js";
|
|
@@ -128,8 +133,8 @@ import { lastSweep, saveSweep, storageSweepOff } from "../storage-sweep.js";
|
|
|
128
133
|
import { sweepSchedule } from "../storage-sweep-schedule.js";
|
|
129
134
|
import { storageHtml } from "../storage-ui.js";
|
|
130
135
|
import { landedSummary } from "../landed-cleanup.js";
|
|
131
|
-
import { bytesWords, parseCleanup } from "../storage.js";
|
|
132
|
-
import { isDigestTime, liveBackupState, localModeOf, RESULT_SCREENSHOTS, setLiveBackupSettings, setLocalModeOff } from "../store.js";
|
|
136
|
+
import { bytesWords, parseCleanup, parseIdle } from "../storage.js";
|
|
137
|
+
import { checkoutIdle, isDigestTime, liveBackupState, setCheckoutIdle, localModeOf, RESULT_SCREENSHOTS, setLiveBackupSettings, setLocalModeOff } from "../store.js";
|
|
133
138
|
import { defaultLiveFolder, liveFolderOf, liveFolderProblem } from "../live-backup.js";
|
|
134
139
|
import { LIVE_EVERY } from "../backup-ui.js";
|
|
135
140
|
import { checkTeamsCredentials, clearTeamsCredentials, loadTeamsCredentials, saveTeamsCredentials, TeamsError } from "../teams-api.js";
|
|
@@ -353,9 +358,34 @@ ${connectionHtml(asked, connection, task)}${command === null ? "" : html `<p>On
|
|
|
353
358
|
return refuse(response, who, 403, "That project is outside your access.", "/settings/project");
|
|
354
359
|
const selector = projectPicker("/settings/project", projects.map(repo => ({ repo, name: projectName(repo) })), chosen);
|
|
355
360
|
const notice = { said: url.searchParams.get("said"), problem: url.searchParams.get("problem") };
|
|
356
|
-
const content = chosen === "" ? html `${notice.said ? infoLine(notice.said) : ""}${empty({ title: "No projects yet.", action: btn("Add a project", { sm: true, href: "/projects" }) })}` : (projectSettingsHtml({ ...projectViewOf(chosen, who), remoteUrl: store.projectRemote(chosen) }, notice));
|
|
361
|
+
const content = chosen === "" ? html `${notice.said ? infoLine(notice.said) : ""}${empty({ title: "No projects yet.", action: btn("Add a project", { sm: true, href: "/projects" }) })}` : (projectSettingsHtml({ ...projectViewOf(chosen, who), remoteUrl: store.projectRemote(chosen), codeMap: codeMapView(chosen, who, ctx.now) }, notice));
|
|
357
362
|
return sendScreen(response, 200, screen("Project", settingsSubPage("projects", "Project", html `${selector}${content}`), { chrome: chromeFor(chosen || null, "settings") }));
|
|
358
363
|
}
|
|
364
|
+
/** The project's code map section: the switch, and the holdout while new builds are still being enrolled. */
|
|
365
|
+
function codeMapView(repo, who, now) {
|
|
366
|
+
const row = store.handle.prepare("SELECT code_map, code_map_enroll_until FROM project WHERE path = ?").get(repo);
|
|
367
|
+
const until = typeof row?.["code_map_enroll_until"] === "string" ? row["code_map_enroll_until"] : null;
|
|
368
|
+
return { on: row === undefined || Number(row["code_map"]) === 1, enrollUntil: until !== null && Date.parse(until) > now.getTime() ? until : null,
|
|
369
|
+
canEndHoldout: who.via === "cookie" && who.authority !== "local-owner", href: `/projects/${encodeURIComponent(projectName(repo))}/map` };
|
|
370
|
+
}
|
|
371
|
+
function codeMapAct(kind) {
|
|
372
|
+
return async (ctx) => {
|
|
373
|
+
const { response, now, posted } = ctx;
|
|
374
|
+
const who = browserCaller(ctx.who);
|
|
375
|
+
const repo = posted.get("repo") ?? "";
|
|
376
|
+
const back = (key, words) => redirect(response, `/settings/project?repo=${encodeURIComponent(repo)}&${key}=${encodeURIComponent(words)}#code-map`);
|
|
377
|
+
if (posted.getAll("repo").length !== 1 || posted.getAll("on").length > 1)
|
|
378
|
+
return back("problem", "That form was sent twice over. Nothing changed.");
|
|
379
|
+
if (!consoleProjects().includes(repo) || !store.accountCanAccess(who.name, repo))
|
|
380
|
+
return refuse(response, who, 403, "That project is outside your access.", "/settings/project");
|
|
381
|
+
const door = who.authority === "local-owner" ? { kind: "local-owner", actor: who.name } : { kind: "person", person: modelsAuthority(who, "session") };
|
|
382
|
+
const on = posted.get("on") === "1";
|
|
383
|
+
const done = kind === "switch" ? setCodeMapSwitch(store, door, repo, on, now) : endHoldout(store, door, repo, now);
|
|
384
|
+
if (!done.ok)
|
|
385
|
+
return back("problem", done.problem);
|
|
386
|
+
return back("said", kind === "holdout" ? "The holdout has ended. Builds already enrolled keep their arm." : on ? "The code map is on." : "The code map is off.");
|
|
387
|
+
};
|
|
388
|
+
}
|
|
359
389
|
// v102: Settings → Approvals and safety: the defaults for new tasks, a project's separation of duties and release
|
|
360
390
|
// approval, and the way to its operating mode. Anyone who sees the project reads them; each form's own route decides
|
|
361
391
|
// who may change it (an approver for the defaults and releases, an instance operator for the rules).
|
|
@@ -467,9 +497,9 @@ ${connectionHtml(asked, connection, task)}${command === null ? "" : html `<p>On
|
|
|
467
497
|
const measured = lastChecks.known("storage");
|
|
468
498
|
const check = postForm("/settings/storage/check", btn("Check now", { sm: true, type: "submit" }));
|
|
469
499
|
if (measured === null) {
|
|
470
|
-
return sendScreen(response, 200, screen("Storage", settingsSubPage("system", "Storage", settingsSection({ id: "storage", title: "
|
|
500
|
+
return sendScreen(response, 200, screen("Storage", settingsSubPage("system", "Storage", settingsSection({ id: "storage", title: "Task folders not measured yet", lead: "Toolroll measures the task folders in the background. Check now measures them at once.", body: check })), { chrome: chromeFor(project, "settings") }));
|
|
471
501
|
}
|
|
472
|
-
const plan = { ...measured.value, cleanup: store.checkoutCleanup() };
|
|
502
|
+
const plan = { ...measured.value, cleanup: store.checkoutCleanup(), idle: checkoutIdle(store) };
|
|
473
503
|
const sweep = { last: lastSweep(store), off: storageSweepOff(), schedule: sweepSchedule(store, now) };
|
|
474
504
|
return sendScreen(response, 200, screen("Storage", settingsSubPage("system", "Storage", html `${(storageHtml({ plan, csrf: who.session.csrf, sweep, landed: landedSummary(store), measured: html `<div class="k-acts"><span class="k-note">Measured ${new Date(measured.at).toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" })}</span>${check}</div>` }, { said: url.searchParams.get("said"), problem: url.searchParams.get("problem") }))}`), { chrome: chromeFor(project, "settings") }));
|
|
475
505
|
}
|
|
@@ -499,7 +529,7 @@ ${connectionHtml(asked, connection, task)}${command === null ? "" : html `<p>On
|
|
|
499
529
|
const quick = liveQuickCommand(store, repo);
|
|
500
530
|
const body = (checkSettingsHtml({ repo, name: projectName(repo), csrf: who.session.csrf, canChange: who.role === "approver" && !store.isDemo(),
|
|
501
531
|
level: projectCheckLevel(store, repo).level, full: store.liveVerifyCommand(repo), quick, suggestion: quick === null ? suggestQuickCommand(repo) : null,
|
|
502
|
-
review: (({ on, source }) => ({ on, source }))(store.reviewSwitch(repo, clock())), batch: { on: projectBatchChecks(store, repo).on },
|
|
532
|
+
review: (({ on, source }) => ({ on, source }))(store.reviewSwitch(repo, clock())), batch: { on: projectBatchChecks(store, repo).on }, fast: { on: fastStartOn(store, repo) },
|
|
503
533
|
said: url.searchParams.get("said"), problem: url.searchParams.get("problem") }));
|
|
504
534
|
return sendScreen(response, 200, screen("Checks", settingsSubPage("projects", "Checks", html `${body}`), { chrome: chromeFor(project, "projects") }));
|
|
505
535
|
}
|
|
@@ -577,21 +607,25 @@ ${connectionHtml(asked, connection, task)}${command === null ? "" : html `<p>On
|
|
|
577
607
|
const revision = Number(url.searchParams.get('version'));
|
|
578
608
|
const previous = knowledgeVersion(store, chosen, who.name, revision);
|
|
579
609
|
const restore = csrf && who.role === 'approver' && revision !== view.revision ? postForm('/settings/knowledge/change', html `<p>Replaces knowledge for future tasks. Existing runs keep their saved context.</p><button>Restore version ${revision}</button>`, { attrs: { class: 'knowledge' }, hidden: { repo: chosen, identity: view.identity, revision: String(view.revision), action: 'restore', restore: String(revision) } }) : '';
|
|
580
|
-
return sendScreen(response, 200, screen('Knowledge history', settingsSubPage('agents', `Knowledge · version ${revision}`, html `<p class="k-note"><a class="k-link" href="/settings/knowledge?repo=${encodeURIComponent(chosen)}">Current knowledge</a></p>${knowledgeHtml({ ...view, knowledge: previous, history: [], stale: null }, '', false)}${restore}`), { chrome: chromeFor(chosen, 'settings') }));
|
|
610
|
+
return sendScreen(response, 200, screen('Knowledge history', settingsSubPage('agents', `Knowledge · version ${revision}`, html `<p class="k-note"><a class="k-link" href="/settings/knowledge?repo=${encodeURIComponent(chosen)}">Current knowledge</a></p>${knowledgeHtml({ ...view, knowledge: previous, history: [], stale: null }, '', false)}${restore}${referenceRestoreHtml(chosen, revision, previous.references.filter(ref => !view.knowledge.references.some(now => now.id === ref.id)), !!csrf && who.role === 'approver')}`), { chrome: chromeFor(chosen, 'settings') }));
|
|
581
611
|
}
|
|
582
612
|
const query = (url.searchParams.get('q') ?? '').slice(0, 1000);
|
|
583
|
-
const result = query.trim() ? repositoryContextRead({ repo: chosen, query, mode: url.searchParams.get('mode') === 'impact' ? 'impact' : 'search', cacheRoot: join(dirname(evidenceRoot), 'repository-context') }) : null;
|
|
613
|
+
const result = query.trim() ? repositoryContextRead({ repo: chosen, project: chosen, query, mode: url.searchParams.get('mode') === 'impact' ? 'impact' : 'search', cacheRoot: join(dirname(evidenceRoot), 'repository-context') }) : null;
|
|
584
614
|
const memoryQuery = (url.searchParams.get('memory') ?? '').slice(0, 300);
|
|
585
615
|
const hits = memoryQuery.trim() ? searchMemory(store, { actor: who.name, repos: [chosen], query: memoryQuery, limit: 20 }) : null;
|
|
586
616
|
let proposals = html ``;
|
|
587
617
|
try {
|
|
588
|
-
proposals = (proposalsHtml(chosen, listProposals(store, chosen), memoryStatus(store, chosen, who.name), csrf, who.role === 'approver'));
|
|
618
|
+
proposals = (proposalsHtml(chosen, listProposals(store, chosen), memoryStatus(store, chosen, who.name), csrf, who.role === 'approver', who.name));
|
|
589
619
|
}
|
|
590
620
|
catch {
|
|
591
621
|
proposals = html ``;
|
|
592
622
|
}
|
|
593
|
-
|
|
594
|
-
|
|
623
|
+
const use = memoryUseOf(store, chosen);
|
|
624
|
+
const manageMemory = who.role === 'approver' && csrf !== '';
|
|
625
|
+
const retired = listDecisions(store, chosen, who.name, { status: 'all', limit: 100 }).filter(d => d.status !== 'active');
|
|
626
|
+
content = html `<p class="meta">${MEMORY_INTRO}</p>${memorySearchHtml(chosen, memoryQuery, hits)}${proposals}${cleanUpHtml(chosen, listTidy(store, chosen), manageMemory)}${(decisionsHtml(chosen, listDecisions(store, chosen, who.name, { limit: 50 }), csrf, who.role === 'approver', undefined, undefined, use, d => { const after = noteAfterwards(store, d); return after === null ? null : afterwardsWords(after); }))}\
|
|
627
|
+
${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf : '')}${knowledgeHtml(view, csrf, who.role === 'approver', undefined, undefined, use)}\
|
|
628
|
+
${restoreHtml(chosen, retired, undoableMerges(store, chosen), disabledLessons(store, chosen), manageMemory)}\
|
|
595
629
|
<details class="knowledge frame"><summary class="frame-head">Learned lessons</summary><div class="frame-panel">${(learningHtml(learningView(store, evidenceRoot, chosen, who.name), csrf, who.role === 'approver'))}</div></details>`;
|
|
596
630
|
}
|
|
597
631
|
catch {
|
|
@@ -803,11 +837,11 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
803
837
|
const { url, who, response } = ctx;
|
|
804
838
|
const provider = url.pathname.split("/")[2];
|
|
805
839
|
if (!isProviderId(provider))
|
|
806
|
-
return refuse(response, who, 400, "unknown provider", "/
|
|
840
|
+
return refuse(response, who, 400, "unknown provider", "/tasks");
|
|
807
841
|
if (who.role !== "approver")
|
|
808
|
-
return refuse(response, who, 403, "Only an operator can resume an agent's work.", "/
|
|
842
|
+
return refuse(response, who, 403, "Only an operator can resume an agent's work.", "/tasks");
|
|
809
843
|
liftAuthPause(store, provider, "person", who.name, clock());
|
|
810
|
-
return redirect(response, "/
|
|
844
|
+
return redirect(response, "/tasks");
|
|
811
845
|
}
|
|
812
846
|
// The update notice is dismissed per browser and per version: a newer release shows it again.
|
|
813
847
|
// Cockpit B2: Forget — nothing verified on this session before now counts as fresh (a CSRF-checked post, like every act).
|
|
@@ -1235,16 +1269,20 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1235
1269
|
// What the page shows next is measured again after the change.
|
|
1236
1270
|
const said = sayTo(response, "/settings/storage");
|
|
1237
1271
|
const back = async (kind, words) => { await measureStorage(databaseFile).catch(() => undefined); return said(kind, words); };
|
|
1238
|
-
const what = op === "clean" ? "clean up" : op === "discard" ? "discard a
|
|
1272
|
+
const what = op === "clean" ? "clean up" : op === "discard" ? "discard a task folder" : "change automatic cleanup";
|
|
1239
1273
|
if (!authenticateApprover(who, body.get("password") ?? "").ok)
|
|
1240
1274
|
return back("problem", `Enter your Toolroll password to ${what}.`);
|
|
1241
1275
|
if (op === "cleanup") {
|
|
1242
1276
|
const cleanup = parseCleanup(body.get("cleanup") ?? "");
|
|
1243
1277
|
if (cleanup === undefined)
|
|
1244
|
-
return back("problem", "Choose when a finished task's
|
|
1245
|
-
const
|
|
1278
|
+
return back("problem", "Choose when a finished task's folder is removed.");
|
|
1279
|
+
const idle = body.get("idle") === null ? checkoutIdle(store) : parseIdle(body.get("idle") ?? "");
|
|
1280
|
+
if (idle === undefined)
|
|
1281
|
+
return back("problem", "Choose when an idle task's folder is removed.");
|
|
1282
|
+
const same = cleanup === store.checkoutCleanup() && idle === checkoutIdle(store);
|
|
1246
1283
|
store.setCheckoutCleanup(cleanup, who.name, now);
|
|
1247
|
-
|
|
1284
|
+
setCheckoutIdle(store, idle, who.name, now);
|
|
1285
|
+
return back("said", same ? "Nothing changed." : "Saved.");
|
|
1248
1286
|
}
|
|
1249
1287
|
const pool = storagePool(databaseFile);
|
|
1250
1288
|
if (op === "clean") {
|
|
@@ -1256,7 +1294,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1256
1294
|
return back("problem", "What a clean-up would remove changed since you looked. Check the list again.");
|
|
1257
1295
|
const done = await cleanCheckouts(store, pool, clock, { manual: true, actor: who.name, only: new Set(plan.go.map(one => one.path)) });
|
|
1258
1296
|
saveSweep(store, { at: clock().toISOString(), source: "manual", actor: who.name, parts: [{ kind: "checkouts", count: done.removed.length, bytes: done.freed, failed: done.kept.filter(one => one.why === "git refused").length, items: done.removed.slice(0, 20).map(one => one.path) }] });
|
|
1259
|
-
return back("said", done.removed.length === 0 ? "Nothing was removed: those
|
|
1297
|
+
return back("said", done.removed.length === 0 ? "Nothing was removed: those task folders are no longer ready to go." : `Removed ${done.removed.length} task folder${done.removed.length === 1 ? "" : "s"}, about ${bytesWords(done.freed)}. Their branches stay.`);
|
|
1260
1298
|
}
|
|
1261
1299
|
const done = await discardCheckout(store, pool, body.get("path") ?? "", now, who.name);
|
|
1262
1300
|
return done.ok ? back("said", `Discarded, about ${bytesWords(done.bytes)}. Its branch stays.`) : back("problem", done.message);
|
|
@@ -1331,6 +1369,13 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1331
1369
|
const changed = setProjectBatchChecks(store, repo, on, who.name, now);
|
|
1332
1370
|
return back("said", !changed.changed ? "Saved." : on ? "Batch checks are on." : "Batch checks are off.");
|
|
1333
1371
|
}
|
|
1372
|
+
// Small tasks start right away: a person's own logged settings act, behind this form's password.
|
|
1373
|
+
if (act === "small-tasks") {
|
|
1374
|
+
const on = body.get("on") === "1";
|
|
1375
|
+
store.upsertProject(repo, projectName(repo), now);
|
|
1376
|
+
setFastStart(store, repo, on, who.name, now);
|
|
1377
|
+
return back("said", on ? "Small tasks start right away." : "Small tasks wait for a plan.");
|
|
1378
|
+
}
|
|
1334
1379
|
if (act === "level") {
|
|
1335
1380
|
const level = body.get("level");
|
|
1336
1381
|
if (!isCheckLevel(level))
|
|
@@ -1736,7 +1781,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1736
1781
|
const repo = body.get('repo') ?? '';
|
|
1737
1782
|
if (!visible(repo) || !store.accountCanAccess(who.name, repo) || !consoleProjects().includes(repo))
|
|
1738
1783
|
return refuse(response, who, 403, 'That project is outside your access.', '/settings/knowledge');
|
|
1739
|
-
const refreshed = await repositoryContext({ repo, query: '', refresh: true, cacheRoot: join(dirname(evidenceRoot), 'repository-context') });
|
|
1784
|
+
const refreshed = await repositoryContext({ repo, project: repo, query: '', refresh: true, cacheRoot: join(dirname(evidenceRoot), 'repository-context') });
|
|
1740
1785
|
return sendScreen(response, refreshed.index.status === 'ready' ? 200 : 503, screen('Project context', settingsSubPage("agents", "Project context", html `<p>${refreshed.index.status === 'ready' ? 'Code index refreshed.' : 'The index is unavailable. Source search still works.'}</p><a class="button-link" href="/settings/knowledge?repo=${encodeURIComponent(repo)}">Open knowledge</a>`), { chrome: chromeFor(repo, 'settings') }));
|
|
1741
1786
|
}
|
|
1742
1787
|
async function knowledgeProposal(ctx) {
|
|
@@ -1755,6 +1800,64 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1755
1800
|
}
|
|
1756
1801
|
return redirect(response, `/settings/knowledge?repo=${encodeURIComponent(repo)}&saved=1`);
|
|
1757
1802
|
}
|
|
1803
|
+
/** A lesson note: a person's own words, accepted as a decision through the one proposal writer, or rejected. */
|
|
1804
|
+
async function knowledgeNote(ctx) {
|
|
1805
|
+
const { who, response, now, posted } = ctx;
|
|
1806
|
+
const body = readForm(posted, CONSOLE_FORMS.knowledgeNote);
|
|
1807
|
+
const repo = body.get('repo') ?? '', decision = body.get('decision') ?? '';
|
|
1808
|
+
if (!visible(repo) || !consoleProjects().includes(repo))
|
|
1809
|
+
return refuse(response, who, 403, 'That project is outside your access.', '/settings/knowledge');
|
|
1810
|
+
if (!['accept', 'reject'].includes(decision) || !/^[0-9]{1,12}$/.test(body.get('proposal') ?? ''))
|
|
1811
|
+
return refuse(response, who, 400, 'Choose accept or reject for one note.', '/settings/knowledge');
|
|
1812
|
+
const id = Number(body.get('proposal'));
|
|
1813
|
+
try {
|
|
1814
|
+
if (lessonNoteOf(store, id) === null)
|
|
1815
|
+
throw Error('That note is unavailable.');
|
|
1816
|
+
decideProposal(store, { repo, actor: who.name, id, decision: decision, claim: body.get('claim') ?? '' }, now);
|
|
1817
|
+
}
|
|
1818
|
+
catch (error) {
|
|
1819
|
+
return sendScreen(response, 409, screen('Knowledge', settingsSubPage("agents", "Knowledge", html `<p role="alert">${error instanceof Error ? error.message : 'That note could not be decided.'}</p><p><a class="k-link" href="/settings/knowledge?repo=${encodeURIComponent(repo)}">Back to Knowledge</a></p>`), { chrome: chromeFor(repo, 'settings') }));
|
|
1820
|
+
}
|
|
1821
|
+
return redirect(response, `/settings/knowledge?repo=${encodeURIComponent(repo)}&saved=1`);
|
|
1822
|
+
}
|
|
1823
|
+
/** Project memory's clean-up: a person's request, accepting or rejecting one proposal, and restores. Every writer
|
|
1824
|
+
* proves the person inside its own transaction. */
|
|
1825
|
+
async function knowledgeTidy(ctx) {
|
|
1826
|
+
const { who, response, now, posted } = ctx;
|
|
1827
|
+
const body = readForm(posted, CONSOLE_FORMS.knowledgeTidy);
|
|
1828
|
+
const repo = body.get('repo') ?? '', action = body.get('action') ?? '', target = body.get('target') ?? '';
|
|
1829
|
+
if (!visible(repo) || !consoleProjects().includes(repo))
|
|
1830
|
+
return refuse(response, who, 403, 'That project is outside your access.', '/settings/knowledge');
|
|
1831
|
+
const back = `/settings/knowledge?repo=${encodeURIComponent(repo)}`;
|
|
1832
|
+
const said = (id) => /^[0-9]{1,12}$/.test(id) ? Number(id) : null;
|
|
1833
|
+
// Only a browser session is a person's proof here; the writers refuse anything else.
|
|
1834
|
+
const proof = who.via === 'cookie' ? 'session' : 'remote-token';
|
|
1835
|
+
try {
|
|
1836
|
+
if (action === 'propose')
|
|
1837
|
+
await proposeTidy(store, { repo, actor: who.name, analyzer: defaultTidyAnalyzer(store, store.databaseFile() === null ? {} : { configDir: dirname(store.databaseFile()) }) }, now);
|
|
1838
|
+
else if (action === 'accept' || action === 'reject') {
|
|
1839
|
+
const id = said(body.get('proposal') ?? '');
|
|
1840
|
+
if (id === null)
|
|
1841
|
+
throw Error('Choose one proposal.');
|
|
1842
|
+
const choice = body.get('choice'), claim = body.get('claim'), why = body.get('why');
|
|
1843
|
+
decideTidy(store, evidenceRoot, { repo, actor: who.name, id, decision: action, proof, ...(choice === null ? {} : { choice }), ...(claim === null ? {} : { claim }), ...(why === null ? {} : { why }) }, now);
|
|
1844
|
+
}
|
|
1845
|
+
else if (action === 'restore' && said(target) !== null)
|
|
1846
|
+
restoreDecision(store, { repo, actor: who.name, id: said(target), proof }, now);
|
|
1847
|
+
else if (action === 'unmerge' && said(target) !== null)
|
|
1848
|
+
unmergeDecisions(store, { repo, actor: who.name, merge: said(target), proof }, now);
|
|
1849
|
+
else if (action === 'readopt' && said(target) !== null)
|
|
1850
|
+
restoreLesson(store, evidenceRoot, { repo, actor: who.name, id: said(target), proof }, now);
|
|
1851
|
+
else if (action === 'reference' && /^[a-f0-9]{20}$/.test(target) && said(body.get('revision') ?? '') !== null)
|
|
1852
|
+
restoreReference(store, { repo, actor: who.name, id: target, fromRevision: said(body.get('revision') ?? ''), proof }, now);
|
|
1853
|
+
else
|
|
1854
|
+
return refuse(response, who, 400, 'Choose one clean-up action.', back);
|
|
1855
|
+
}
|
|
1856
|
+
catch (error) {
|
|
1857
|
+
return sendScreen(response, 409, screen('Knowledge', settingsSubPage("agents", "Knowledge", html `<p role="alert">${error instanceof Error ? error.message : 'That could not be done.'}</p><p><a class="k-link" href="${back}">Back to Knowledge</a></p>`), { chrome: chromeFor(repo, 'settings') }));
|
|
1858
|
+
}
|
|
1859
|
+
return redirect(response, `${back}&saved=1`);
|
|
1860
|
+
}
|
|
1758
1861
|
async function knowledgeDecision(ctx) {
|
|
1759
1862
|
const { response, now, posted } = ctx;
|
|
1760
1863
|
const who = browserCaller(ctx.who);
|
|
@@ -2754,6 +2857,8 @@ ${postForm("/settings/chat-approval/save", html `\
|
|
|
2754
2857
|
"settings.models-stop": modelsRuleAct("stop"),
|
|
2755
2858
|
"settings.knowledge-refresh": knowledgeRefresh,
|
|
2756
2859
|
"settings.knowledge-proposal": knowledgeProposal,
|
|
2860
|
+
"settings.knowledge-note": knowledgeNote,
|
|
2861
|
+
"settings.knowledge-tidy": knowledgeTidy,
|
|
2757
2862
|
"settings.knowledge-decision": knowledgeDecision,
|
|
2758
2863
|
"settings.knowledge-change": knowledgeChange,
|
|
2759
2864
|
"settings.learning-change": learningChange,
|
|
@@ -2803,6 +2908,8 @@ ${postForm("/settings/chat-approval/save", html `\
|
|
|
2803
2908
|
"settings.lead-about": leadAbout,
|
|
2804
2909
|
"settings.lead-promise-cancel": leadPromiseCancel,
|
|
2805
2910
|
"settings.lead-on": leadOn,
|
|
2911
|
+
"settings.code-map": codeMapAct("switch"),
|
|
2912
|
+
"settings.code-map-holdout": codeMapAct("holdout"),
|
|
2806
2913
|
}, {
|
|
2807
2914
|
"edge.google-callback": googleCallback,
|
|
2808
2915
|
"edge.connect-callback": connectCallback,
|