toolroll 0.9.83 → 0.9.85
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 +69 -0
- package/README.md +94 -1192
- package/dist/agent-fence.d.ts +3 -3
- package/dist/agent-fence.js +3 -3
- package/dist/agent-onboard.d.ts +2 -0
- package/dist/agent-onboard.js +3 -0
- package/dist/assignment-ui.js +5 -1
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +31 -31
- package/dist/browser-crew.js +5 -1
- package/dist/browser-workspace.d.ts +12 -0
- package/dist/chat-channel.d.ts +10 -1
- package/dist/chat-channel.js +35 -5
- package/dist/chat-decide.d.ts +16 -4
- package/dist/chat-decide.js +239 -31
- package/dist/chat-messages.d.ts +5 -0
- package/dist/chat-messages.js +1 -1
- package/dist/chat-quiet.js +3 -2
- package/dist/chat-review.d.ts +4 -0
- package/dist/chat-review.js +13 -4
- package/dist/chat-terms.d.ts +9 -0
- package/dist/chat-terms.js +46 -0
- package/dist/contracts/console-api.d.ts +4 -2
- package/dist/contracts/console-api.js +7 -2
- package/dist/contracts/lead-tools.d.ts +30 -0
- package/dist/contracts/lead-tools.js +3 -0
- package/dist/contracts/youiyoux-request.d.ts +248 -0
- package/dist/contracts/youiyoux-request.js +152 -0
- package/dist/control-setup.d.ts +9 -0
- package/dist/control-setup.js +31 -4
- package/dist/control-ui.d.ts +7 -1
- package/dist/control-ui.js +3 -2
- package/dist/desktop-update.d.ts +1 -1
- package/dist/dispatch.d.ts +15 -0
- package/dist/dispatch.js +26 -4
- package/dist/first-run.d.ts +5 -2
- package/dist/first-run.js +13 -2
- package/dist/flow-cadence.d.ts +8 -2
- package/dist/flow-cadence.js +16 -3
- package/dist/flow-card-task.d.ts +9 -0
- package/dist/flow-card-task.js +15 -6
- package/dist/folder-look.d.ts +3 -0
- package/dist/folder-look.js +8 -4
- package/dist/guides.js +4 -3
- package/dist/integrations-ui.js +9 -4
- package/dist/integrations.d.ts +9 -1
- package/dist/integrations.js +43 -2
- package/dist/landed-cleanup.d.ts +4 -0
- package/dist/landed-cleanup.js +3 -2
- package/dist/lead-capabilities.js +2 -1
- package/dist/lead-cli.js +3 -1
- package/dist/lead-tools.d.ts +2 -0
- package/dist/lead-tools.js +30 -4
- package/dist/lead.d.ts +2 -0
- package/dist/lead.js +3 -3
- package/dist/model-catalog.d.ts +5 -0
- package/dist/model-catalog.js +11 -2
- package/dist/model-choices.d.ts +188 -0
- package/dist/model-choices.js +405 -0
- package/dist/model-names.d.ts +6 -0
- package/dist/model-names.js +34 -0
- package/dist/models-ui.d.ts +69 -5
- package/dist/models-ui.js +53 -10
- package/dist/needs-you.d.ts +8 -0
- package/dist/needs-you.js +8 -0
- package/dist/operate.d.ts +22 -2
- package/dist/operate.js +107 -41
- package/dist/project-activity.d.ts +20 -0
- package/dist/project-activity.js +23 -0
- package/dist/project-admission.d.ts +4 -1
- package/dist/project-admission.js +23 -2
- package/dist/project-cli.d.ts +1 -0
- package/dist/project-cli.js +36 -19
- package/dist/project-identity.d.ts +9 -2
- package/dist/project-identity.js +80 -33
- package/dist/project-knowledge.d.ts +9 -9
- package/dist/project-outcomes.d.ts +119 -0
- package/dist/project-outcomes.js +79 -0
- package/dist/result-actions.d.ts +4 -0
- package/dist/result-actions.js +41 -9
- package/dist/result-review.d.ts +9 -0
- package/dist/result-review.js +45 -5
- package/dist/serve.js +60 -13
- package/dist/server/chat.js +5 -4
- package/dist/server/chrome.d.ts +3 -0
- package/dist/server/chrome.js +5 -2
- package/dist/server/console.css +17 -2
- package/dist/server/flows.js +4 -0
- package/dist/server/http.d.ts +1 -0
- package/dist/server/http.js +6 -0
- package/dist/server/pages.js +53 -7
- package/dist/server/people-tokens.js +40 -20
- package/dist/server/render-chat.js +3 -2
- package/dist/server/render-pages.d.ts +12 -2
- package/dist/server/render-pages.js +36 -16
- package/dist/server/render-people.d.ts +14 -2
- package/dist/server/render-people.js +35 -12
- package/dist/server/render-results.d.ts +2 -0
- package/dist/server/render-results.js +20 -7
- package/dist/server/render-settings.js +1 -0
- package/dist/server/render-tasks.d.ts +1 -0
- package/dist/server/render-tasks.js +22 -19
- package/dist/server/request-context.d.ts +2 -0
- package/dist/server/route-table.js +9 -1
- package/dist/server/settings.d.ts +58 -1
- package/dist/server/settings.js +216 -70
- package/dist/server/signup-gate.d.ts +9 -0
- package/dist/server/signup-gate.js +40 -0
- package/dist/server/task-views.js +6 -0
- package/dist/server/tasks.js +54 -8
- package/dist/settings-kit-script.d.ts +21 -0
- package/dist/settings-kit-script.js +67 -0
- package/dist/settings-kit.d.ts +137 -0
- package/dist/settings-kit.js +186 -0
- package/dist/settings-proofs.d.ts +64 -0
- package/dist/settings-proofs.js +101 -0
- package/dist/setup-guide.js +4 -2
- package/dist/spend-report.d.ts +87 -0
- package/dist/spend-report.js +265 -0
- package/dist/spend-ui.d.ts +29 -8
- package/dist/spend-ui.js +74 -39
- package/dist/store.d.ts +46 -3
- package/dist/store.js +165 -6
- package/dist/task-sizing.d.ts +5 -3
- package/dist/task-sizing.js +4 -4
- package/dist/task-source.d.ts +13 -1
- package/dist/task-source.js +17 -1
- package/dist/telegram-lead.js +7 -5
- package/dist/telegram.js +1 -1
- package/dist/toolroll-update.js +2 -2
- package/dist/webhooks.d.ts +6 -0
- package/dist/webhooks.js +25 -0
- package/dist/work-index.d.ts +4 -0
- package/dist/work-index.js +32 -0
- package/dist/youiyoux-intake.d.ts +234 -0
- package/dist/youiyoux-intake.js +908 -0
- package/package.json +11 -8
package/dist/guides.js
CHANGED
|
@@ -287,9 +287,10 @@ ${AUTHORITY_LINE}
|
|
|
287
287
|
|
|
288
288
|
- One command, inside the repository: \`npx toolroll up\` (or
|
|
289
289
|
\`bunx toolroll up\`; Node 22.13+ must be installed — Bun's own
|
|
290
|
-
runtime cannot host it). \`up\` mints the first login and
|
|
291
|
-
|
|
292
|
-
|
|
290
|
+
runtime cannot host it). \`up\` mints the first login and saves it beside the
|
|
291
|
+
database as \`up-login.txt\` (every start prints that path and the
|
|
292
|
+
\`cat\` command that shows the password, never the password itself),
|
|
293
|
+
registers the machine as a worker, runs the console, opens the browser. Later starts ask for the
|
|
293
294
|
password at most once and remember it on that machine.
|
|
294
295
|
- From a phone over a tailnet: add \`--host 0.0.0.0 --allow-host
|
|
295
296
|
<name>:4180\`.
|
package/dist/integrations-ui.js
CHANGED
|
@@ -14,7 +14,7 @@ export const INTEGRATIONS_CSS = `.integrations{max-width:760px;min-width:0}.inte
|
|
|
14
14
|
`.integrations .integration>:not(.integration-action),.integrations .integration-body>*{grid-column:1}.integrations .integration-body{display:contents}.integrations .integration:first-of-type{border-top:0}.integrations .integration-head{margin:0;display:flex;flex-wrap:wrap;gap:6px 10px;align-items:center;min-width:0}` +
|
|
15
15
|
`.integrations .integration .integration-body>*{margin-left:calc(var(--brand-mark) + 10px)}.integrations .integration-head strong{overflow-wrap:anywhere}.integrations .integration p{margin:2px 0 0;min-width:0;overflow-wrap:anywhere}` +
|
|
16
16
|
`.integrations .integration-action{grid-column:2;grid-row:1 / span 3;align-self:center;margin:0}.integrations .integration-action button,.integrations .integration-action .button-link{min-height:44px;white-space:nowrap}` +
|
|
17
|
-
`.integrations .integration .integration-action button{width:auto}.integrations .integration .integration-action a.integration-quiet{background:var(--so-paper);color:var(--so-ink);border:1px solid var(--so-input-line)}` +
|
|
17
|
+
`.integrations .integration .integration-action button{width:auto}.integrations .integration .integration-action a.integration-quiet,.integrations .integration .integration-action button.integration-quiet{background:var(--so-paper);color:var(--so-ink);border:1px solid var(--so-input-line)}` +
|
|
18
18
|
`.integrations .integration-fix{color:var(--so-danger)}.integrations code{overflow-wrap:anywhere}.integrations details{margin-top:4px;font-size:.8125rem}.integrations summary{cursor:pointer;padding:4px 0}` +
|
|
19
19
|
`.integration-state{display:inline-flex;align-items:center;gap:6px;font-size:.75rem;font-weight:500;padding:0 8px;border-radius:5px;line-height:20px;white-space:nowrap}` +
|
|
20
20
|
`.integration-state i{width:6px;height:6px;border-radius:50%;background:currentColor}` +
|
|
@@ -33,6 +33,10 @@ function action(one, csrf) {
|
|
|
33
33
|
if (a.kind === "test") {
|
|
34
34
|
return csrf === "" ? "" : postForm("/settings/integrations/test", html `<button type="submit">Send test</button>`, { attrs: { class: "integration-action" }, hidden: { key: one.key } });
|
|
35
35
|
}
|
|
36
|
+
// A project's youiyoux requests: one switch for that project.
|
|
37
|
+
if (a.kind === "switch") {
|
|
38
|
+
return csrf === "" ? "" : postForm("/settings/integrations/youiyoux", html `<button type="submit"${a.on ? "" : html ` class="integration-quiet"`}>${a.label}</button>`, { attrs: { class: "integration-action" }, hidden: { repo: a.repo, on: a.on ? "1" : "0" } });
|
|
39
|
+
}
|
|
36
40
|
// A command is run where Toolroll runs; a page is linked. Fix and Set up both say which.
|
|
37
41
|
// Fix is the one filled action on the page; Set up stays quiet so a list of unused services doesn't shout.
|
|
38
42
|
if (a.href !== null)
|
|
@@ -50,12 +54,13 @@ function row(one, csrf) {
|
|
|
50
54
|
? html `<span class="integration-state integration-state--checking"><i aria-hidden="true"></i>Checking</span>`
|
|
51
55
|
: html `<span class="integration-state integration-state--${one.state}"><i aria-hidden="true"></i>${STATE_WORDS[one.state]}</span>`;
|
|
52
56
|
const facts = [one.account, one.detail, one.credentials].filter(Boolean).join(" · ");
|
|
53
|
-
const command = one.action.kind
|
|
54
|
-
const
|
|
57
|
+
const command = one.action.kind === "setup" || one.action.kind === "fix" ? one.action.command : null;
|
|
58
|
+
const words = one.action.kind === "fix" || one.action.kind === "switch" ? one.action.words : null;
|
|
59
|
+
const fix = words !== null ? html `<p class="integration-fix" role="status">${words}</p>` : "";
|
|
55
60
|
const run = command === null || (one.action.kind === "fix" && one.action.words.includes(command)) ? "" : html `<p class="meta">Run <code>${command}</code> on the computer running Toolroll.</p>`;
|
|
56
61
|
const seen = [one.lastSuccessAt === null ? null : html `Last success ${when(one.lastSuccessAt)}`, one.usedBy.length === 0 ? null : html `Used by ${one.usedBy.join(", ")}`].filter(Boolean);
|
|
57
62
|
const history = one.state === "not-set-up" || seen.length === 0 ? "" : html `<p class="meta">${joinHtml(seen, " · ")}</p>`;
|
|
58
|
-
const lastError = one.lastError === null || one.state === "not-set-up" ? "" : one.state === "broken" &&
|
|
63
|
+
const lastError = one.lastError === null || one.state === "not-set-up" ? "" : one.state === "broken" && words === one.lastError
|
|
59
64
|
? html `<p class="meta">Failed ${when(one.lastErrorAt ?? "")}</p>`
|
|
60
65
|
: html `<details><summary>Last error ${one.lastErrorAt === null ? "" : when(one.lastErrorAt)}</summary><p class="meta">${one.lastError}</p></details>`;
|
|
61
66
|
return html `<div class="integration" data-integration="${one.key}" data-state="${one.state}"><p class="integration-head">${brandMarkHtml(markFor(one), one.name, one.state === "connected")}<strong>${one.name}</strong> ${badge}</p>${action(one, csrf)}<div class="integration-body">${facts === "" ? "" : html `<p class="meta">${facts}</p>`}${fix}${run}${history}${lastError}</div></div>`;
|
package/dist/integrations.d.ts
CHANGED
|
@@ -18,7 +18,15 @@ export type IntegrationAction = ({
|
|
|
18
18
|
kind: "fix";
|
|
19
19
|
label: "Fix";
|
|
20
20
|
words: string;
|
|
21
|
-
} & IntegrationPlace)
|
|
21
|
+
} & IntegrationPlace)
|
|
22
|
+
/** A project's youiyoux requests: one switch, and the problem to fix when it is Broken. */
|
|
23
|
+
| {
|
|
24
|
+
kind: "switch";
|
|
25
|
+
label: "Turn off" | "Turn on";
|
|
26
|
+
repo: string;
|
|
27
|
+
on: boolean;
|
|
28
|
+
words: string | null;
|
|
29
|
+
};
|
|
22
30
|
export type Integration = {
|
|
23
31
|
key: string;
|
|
24
32
|
group: IntegrationGroup;
|
package/dist/integrations.js
CHANGED
|
@@ -31,6 +31,7 @@ import { LINEAR_URL, readHooksBase, readLinearKey } from "./flow-triggers.js";
|
|
|
31
31
|
import { credentialModeOf, projectToolsOf, readToolSecrets, missingSecrets, secretsSetFor, testToolOf } from "./project-tools.js";
|
|
32
32
|
import { origin, readMonitoring } from "./monitoring-settings.js";
|
|
33
33
|
import { targetOf } from "./monitoring.js";
|
|
34
|
+
import { youiyouxState } from "./youiyoux-intake.js";
|
|
34
35
|
export const STATE_WORDS = { connected: "Connected", "not-set-up": "Not set up", broken: "Broken" };
|
|
35
36
|
const NO_ACTIVITY = { okAt: null, error: null, errorAt: null, failing: false };
|
|
36
37
|
const PROBE_MS = 8_000;
|
|
@@ -459,10 +460,46 @@ function combine(target, row) {
|
|
|
459
460
|
}
|
|
460
461
|
return { ...base, ...common, state: "connected", action: { kind: "test", label: "Send test" } };
|
|
461
462
|
}
|
|
463
|
+
/**
|
|
464
|
+
* youiyoux change requests, one row per project that uses youiyoux (youiyoux-intake.ts): Connected with how many of
|
|
465
|
+
* its requests are queued, Broken when the last pass couldn't read its folder or some of its files, or Not set up when
|
|
466
|
+
* a person turned it off; one row Not set up when no project uses it. Read from the store and the folders' presence.
|
|
467
|
+
*/
|
|
468
|
+
function youiyouxIntegrations(io) {
|
|
469
|
+
// In the order every other row's keys come (combine), so `--json` reads alike.
|
|
470
|
+
const row = (key, account, facts, state, action) => ({
|
|
471
|
+
key, group: "code", name: "youiyoux", usedBy: facts.usedBy ?? [], account, detail: facts.detail, checked: true, checkedAt: facts.checkedAt ?? null,
|
|
472
|
+
lastSuccessAt: facts.lastSuccessAt ?? null, lastError: facts.lastError ?? null, lastErrorAt: facts.lastErrorAt ?? null, state, action,
|
|
473
|
+
});
|
|
474
|
+
const rows = io.repos.flatMap((repo) => {
|
|
475
|
+
let state;
|
|
476
|
+
try {
|
|
477
|
+
state = youiyouxState(io.store, repo);
|
|
478
|
+
}
|
|
479
|
+
catch {
|
|
480
|
+
return [];
|
|
481
|
+
}
|
|
482
|
+
if (!state.uses)
|
|
483
|
+
return [];
|
|
484
|
+
const key = `youiyoux:${repo}`, account = basename(repo);
|
|
485
|
+
const turn = (on, words) => ({ kind: "switch", label: on ? "Turn on" : "Turn off", repo, on, words });
|
|
486
|
+
if (state.off)
|
|
487
|
+
return [row(key, account, { detail: "Turned off" }, "not-set-up", turn(true, null))];
|
|
488
|
+
const facts = { usedBy: [`Tasks in ${account}`], detail: `${plural(state.queued, "request")} queued`, checkedAt: state.checkedAt };
|
|
489
|
+
if (state.problem === null)
|
|
490
|
+
return [row(key, account, { ...facts, lastSuccessAt: state.checkedAt }, "connected", turn(false, null))];
|
|
491
|
+
const words = scrubIntegrationText(state.problem);
|
|
492
|
+
return [row(key, account, { ...facts, lastError: words, lastErrorAt: state.checkedAt }, "broken", turn(false, words))];
|
|
493
|
+
});
|
|
494
|
+
return rows.length > 0 ? rows : [row("youiyoux", null, { detail: "Starts by itself in a project with a youi.project.json" }, "not-set-up", { kind: "setup", label: "Set up", href: null, command: null })];
|
|
495
|
+
}
|
|
462
496
|
/** Every integration as it stands now, from files and saved history only: no network, no process, never slow. */
|
|
463
497
|
export function integrationsNow(io) {
|
|
464
498
|
const rows = readIntegrationChecks(io.store);
|
|
465
|
-
|
|
499
|
+
const list = targets(io).map(target => combine(target, rows.get(target.key)));
|
|
500
|
+
const linear = list.findIndex(one => one.key === "linear");
|
|
501
|
+
list.splice(linear + 1, 0, ...youiyouxIntegrations(io));
|
|
502
|
+
return list;
|
|
466
503
|
}
|
|
467
504
|
/** Run the checks (all, or the named ones), record each answer, and return the list. */
|
|
468
505
|
export async function checkIntegrations(io, keys) {
|
|
@@ -512,7 +549,11 @@ export function renderIntegrations(list, secrets = null) {
|
|
|
512
549
|
lines.push(`${"".padEnd(width)} last success ${whenText(one.lastSuccessAt)}${one.lastError === null ? "" : ` · last error ${whenText(one.lastErrorAt)}: ${one.lastError}`}`);
|
|
513
550
|
if (one.action.kind === "fix")
|
|
514
551
|
lines.push(`${"".padEnd(width)} Fix: ${one.action.words}${one.action.command === null || one.action.words.includes(one.action.command) ? "" : ` (run ${one.action.command})`}`);
|
|
515
|
-
if (one.action.kind === "
|
|
552
|
+
if (one.action.kind === "switch" && one.action.words !== null)
|
|
553
|
+
lines.push(`${"".padEnd(width)} Fix: ${one.action.words}`);
|
|
554
|
+
if (one.action.kind === "switch" && one.action.on)
|
|
555
|
+
lines.push(`${"".padEnd(width)} Turn on: Settings → Integrations`);
|
|
556
|
+
if (one.action.kind === "setup" && (one.action.command !== null || one.action.href !== null))
|
|
516
557
|
lines.push(`${"".padEnd(width)} Set up: ${one.action.command !== null ? `run ${one.action.command}` : `Settings (${one.action.href ?? "/settings"})`}`);
|
|
517
558
|
return lines;
|
|
518
559
|
}), ...note];
|
package/dist/landed-cleanup.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AssignmentSnapshot } from "./assignment.js";
|
|
1
2
|
import type { RemoteMainRead, RemoteMains } from "./remote-main.js";
|
|
2
3
|
import type { Store } from "./store.js";
|
|
3
4
|
import { type Git } from "./teardown-git.js";
|
|
@@ -70,6 +71,9 @@ export type LandedCandidate = {
|
|
|
70
71
|
targets: LandedTarget[];
|
|
71
72
|
keeps: LandedKeep[];
|
|
72
73
|
};
|
|
74
|
+
/** Use the current manual completion guards. Only a recorded merge of this exact result can settle them. Also why a
|
|
75
|
+
* youiyoux options task a person decided on in the hub stays for review (youiyoux-intake.ts). */
|
|
76
|
+
export declare function completionProblem(store: Store, assignment: AssignmentSnapshot, root: string): string | null;
|
|
73
77
|
/**
|
|
74
78
|
* Results in a project whose work landed, with what their teardown would remove and what stays. Reads only.
|
|
75
79
|
* `limit` caps how many results are read against git (reconcile's bound; results read before and unchanged since
|
package/dist/landed-cleanup.js
CHANGED
|
@@ -237,8 +237,9 @@ function resultProblem(store, runId, head) {
|
|
|
237
237
|
return "its result couldn't be read";
|
|
238
238
|
return run.outcome === "no-change" || run.baseRevision === head ? "its result has no new work" : null;
|
|
239
239
|
}
|
|
240
|
-
/** Use the current manual completion guards. Only a recorded merge of this exact result can settle them.
|
|
241
|
-
|
|
240
|
+
/** Use the current manual completion guards. Only a recorded merge of this exact result can settle them. Also why a
|
|
241
|
+
* youiyoux options task a person decided on in the hub stays for review (youiyoux-intake.ts). */
|
|
242
|
+
export function completionProblem(store, assignment, root) {
|
|
242
243
|
if (assignment.state !== "ready-to-check" && assignment.state !== "complete")
|
|
243
244
|
return "it needs a decision first";
|
|
244
245
|
if (assignment.state === "complete")
|
|
@@ -181,7 +181,8 @@ export function integrationCapabilities(list) {
|
|
|
181
181
|
return list.map(one => {
|
|
182
182
|
const link = one.group === "agents" ? "providers" : one.group === "tools" ? "tools" : INTEGRATION_LINK[one.key.replace(/^.*:/, "").toLowerCase()] ?? INTEGRATION_LINK[one.name.toLowerCase()] ?? "integrations";
|
|
183
183
|
const lets = one.usedBy.length > 0 ? `use ${one.name} for ${one.usedBy.join(", ")}` : `use ${one.name}`;
|
|
184
|
-
const next = one.action.kind === "setup" ? `Not set up: set up ${one.name} in Settings` : one.action.kind === "fix" ? `Fix: ${one.action.words}`
|
|
184
|
+
const next = one.action.kind === "setup" ? `Not set up: set up ${one.name} in Settings` : one.action.kind === "fix" ? `Fix: ${one.action.words}`
|
|
185
|
+
: one.action.kind === "switch" ? (one.action.words !== null ? `Fix: ${one.action.words}` : one.action.on ? `Turned off: turn ${one.name} on in Settings → Integrations` : null) : null;
|
|
185
186
|
return { name: one.name, kind: one.group, state: STATE_WORDS[one.state], ok: one.state === "connected", account: one.account, usedBy: one.usedBy, lastError: one.lastError, lets, next, link: next === null ? null : link };
|
|
186
187
|
});
|
|
187
188
|
}
|
package/dist/lead-cli.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseFeedbackImage } from "./result-review.js";
|
|
1
2
|
import { configureLeadFollow, leadFollowStatus } from './lead-follow.js';
|
|
2
3
|
import { closedQuestionWords } from "./decision.js";
|
|
3
4
|
/**
|
|
@@ -104,7 +105,8 @@ export function proposalBlock(store, proposal, repos, number) {
|
|
|
104
105
|
const snapshot = proposal.payload["snapshot"];
|
|
105
106
|
const selected = Array.isArray(proposal.payload["notes"]) ? proposal.payload["notes"] : [];
|
|
106
107
|
const notes = snapshot?.notes.filter(one => selected.includes(one.id)) ?? [];
|
|
107
|
-
|
|
108
|
+
const image = parseFeedbackImage(proposal.payload["image"]);
|
|
109
|
+
return [line, ...(image ? [` Screenshot ${image.artifact} · result #${image.run}`] : []), ...notes.map(one => ` ${one.image ? `Screenshot ${one.image.artifact} · result #${one.image.run}: ` : ""}${one.path === null ? "" : `${one.path}${one.line === null ? "" : `:${one.line}`}: `}${one.note}`),
|
|
108
110
|
proposal.payload["operation"] === "revise" ? " Creates a revision of the same task; approval is separate." : " Saves feedback; no work starts."];
|
|
109
111
|
}
|
|
110
112
|
return proposal.kind === "answer" ? [line, ...answerContextLines(store, proposal.payload)] : [line];
|
package/dist/lead-tools.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FeedbackImage } from "./result-review.js";
|
|
1
2
|
import { type CommitmentChannel } from "./lead-commitments.js";
|
|
2
3
|
import { type Integration } from "./integrations.js";
|
|
3
4
|
import type { AuthMode } from "./keys.js";
|
|
@@ -23,6 +24,7 @@ export type LeadToolContext = {
|
|
|
23
24
|
step: number;
|
|
24
25
|
snapshot: ReviewSnapshot;
|
|
25
26
|
}>;
|
|
27
|
+
replyImage?: FeedbackImage;
|
|
26
28
|
/** Where evidence lives, when the surface knows — a scout's report reads from here. */
|
|
27
29
|
evidenceRoot?: string;
|
|
28
30
|
/** Records the screenshots this turn selected, under the turn, for a channel that delivers files; returns how many are recorded. Absent: nothing durable is kept. */
|
package/dist/lead-tools.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { sameFeedbackImage } from "./result-review.js";
|
|
1
2
|
import { sweepLine, sweepTable } from "./lead-sweep.js";
|
|
2
3
|
import { connectionsOf, oneClickOf } from "./mcp-connect.js";
|
|
3
4
|
import { checkLevelFromWords, isCheckLevel } from "./check-levels.js";
|
|
4
5
|
import { policyParts } from "./policy.js";
|
|
5
6
|
import { randomUUID } from 'node:crypto';
|
|
6
7
|
import { dirname, join } from 'node:path';
|
|
8
|
+
import { projectOutcomes, projectOutcomesPage } from './project-outcomes.js';
|
|
7
9
|
import { repositoryContextRead } from './repository-context.js';
|
|
8
10
|
import { assignmentCatchUp } from './assignment-brief.js';
|
|
9
11
|
import { CHAT_ACTIONS, CHAT_ACTION_FIELDS, isChatAction, prepareSharedAction, sharedActionNeedsReview, sharedActionPayload } from './chat-actions.js';
|
|
@@ -18,6 +20,7 @@ import { cancelCommitment, CHECK_RESULTS, conditionWords, latestResultRun, recor
|
|
|
18
20
|
import { searchMemory } from "./project-memory.js";
|
|
19
21
|
import { integrationsNow, STATE_WORDS } from "./integrations.js";
|
|
20
22
|
import { CAPABILITIES_UNREAD, capabilitiesOf, needsCapabilities } from "./lead-capabilities.js";
|
|
23
|
+
import { feedbackImageProblem } from "./result-actions.js";
|
|
21
24
|
import { readChatResult, reviewInputProblem } from "./chat-review.js";
|
|
22
25
|
import { RESULT_IMAGES_PER_TURN_CAP, selectResultImages } from "./chat-evidence.js";
|
|
23
26
|
import { CHAT_CONTROLS, isChatControl } from "./chat-controls.js";
|
|
@@ -377,7 +380,7 @@ export function agentsOver(store, taskId, now) {
|
|
|
377
380
|
* find_tools and advertised from the next request of the same reply.
|
|
378
381
|
*/
|
|
379
382
|
export const LEAD_CORE_TOOLS = new Set([
|
|
380
|
-
"find_tools", "get_brief", "recap", "list_repos", "get_project_context", "search_project_memory", "get_capabilities",
|
|
383
|
+
"find_tools", "get_brief", "recap", "list_repos", "get_project_context", "get_project_activity", "search_project_memory", "get_capabilities",
|
|
381
384
|
"list_tasks", "get_task", "get_result", "get_diff", "get_check_log", "list_decisions", "get_decision",
|
|
382
385
|
"get_actions", "get_action_status", "propose_action", "propose_task", "propose_scope", "propose_steer",
|
|
383
386
|
"propose_answer", "propose_review", "remember", "ask_owner", "commit_to",
|
|
@@ -475,6 +478,15 @@ const LEAD_TOOL_HANDLERS = {
|
|
|
475
478
|
return { ok: true, body: assignmentCatchUp(ctx.store, ctx.now, { principal: 'operator', repos: ctx.who.repos }, repo === null ? {} : { repo }, ctx.evidenceRoot) };
|
|
476
479
|
},
|
|
477
480
|
},
|
|
481
|
+
get_project_activity: {
|
|
482
|
+
description: 'Read project Activity: current Ready backlog, finished-to-decision elapsed time, explicit revisions and recorded cost for completed families in the last 60 days. Includes coverage and a page of underlying tasks (offset: nextOffset for more); not review labor or a quality score.',
|
|
483
|
+
handle: (ctx, args) => {
|
|
484
|
+
const repo = repoPathOf(ctx.who, args['repo']);
|
|
485
|
+
if (!repo || !ctx.store.accountCanAccess(ctx.who.name, repo))
|
|
486
|
+
return { ok: false, message: 'Choose an available project from list_repos.' };
|
|
487
|
+
return { ok: true, body: projectOutcomesPage(projectOutcomes(ctx.store, repo, ctx.now, { principal: 'operator', repos: ctx.who.repos }), Number(args['offset'] ?? 0)) };
|
|
488
|
+
},
|
|
489
|
+
},
|
|
478
490
|
get_project_context: {
|
|
479
491
|
description: 'Find source excerpts or advisory import impact. Read-only; unavailable indexing falls back to source search.',
|
|
480
492
|
handle: (ctx, args) => {
|
|
@@ -656,6 +668,7 @@ const LEAD_TOOL_HANDLERS = {
|
|
|
656
668
|
changes: snippet.slice(0, 1000), changesShortened: snippet.length > 1000,
|
|
657
669
|
verification: ctx.store.proofVerdictFor(snapshot.run)?.verdict ?? "not verified",
|
|
658
670
|
accepted: ctx.store.proofAcceptance(snapshot.run) !== null,
|
|
671
|
+
images: (snapshot.images ?? []).map(one => ({ id: one.artifact, run: one.run })),
|
|
659
672
|
evidenceTool: "get_acceptance_evidence",
|
|
660
673
|
canRevise: snapshot.execution === snapshot.task } };
|
|
661
674
|
},
|
|
@@ -789,7 +802,7 @@ const LEAD_TOOL_HANDLERS = {
|
|
|
789
802
|
},
|
|
790
803
|
},
|
|
791
804
|
propose_review: {
|
|
792
|
-
description: "Read get_result first. revise uses selected saved_notes plus optional note on the SAME task; note saves only. Omitted saved_notes stays untouched.",
|
|
805
|
+
description: "Read get_result first. revise uses selected saved_notes plus optional note on the SAME task; note saves only. Omitted saved_notes stays untouched. To comment on a screenshot, use its image id from get_result_images. If this channel cannot select one, link to the result Comment control and say feedback still needs submitting there.",
|
|
793
806
|
handle: (ctx, args) => {
|
|
794
807
|
const read = ctx.readResults?.get(Number(args["run"]));
|
|
795
808
|
if (read === undefined || read.step >= ctx.step)
|
|
@@ -812,8 +825,20 @@ const LEAD_TOOL_HANDLERS = {
|
|
|
812
825
|
return { ok: false, message: "Write feedback or select saved notes for a revision." };
|
|
813
826
|
if (operation === "revise" && read.snapshot.task !== read.snapshot.execution)
|
|
814
827
|
return { ok: false, message: "A newer revision is current. Read that version before requesting changes." };
|
|
828
|
+
const wanted = args["image"] === null ? undefined : args["image"] ?? (note !== null && ctx.replyImage?.run === read.snapshot.run ? ctx.replyImage.artifact : undefined);
|
|
829
|
+
let image = null;
|
|
830
|
+
if (wanted !== undefined) {
|
|
831
|
+
image = read.snapshot.images?.find(one => one.artifact === wanted) ?? null;
|
|
832
|
+
if (image === null || ctx.evidenceRoot === undefined || note === null)
|
|
833
|
+
return { ok: false, message: "Choose a saved screenshot from the result you read and write feedback." };
|
|
834
|
+
if (ctx.replyImage?.artifact === wanted && !sameFeedbackImage(ctx.replyImage, image))
|
|
835
|
+
return { ok: false, message: "The screenshot you replied to changed. Open the result and select it again." };
|
|
836
|
+
const problem = feedbackImageProblem(ctx.store, ctx.evidenceRoot, read.snapshot.run, image);
|
|
837
|
+
if (problem !== null)
|
|
838
|
+
return { ok: false, message: problem };
|
|
839
|
+
}
|
|
815
840
|
const id = ctx.draft("review", { task: read.snapshot.task, taskTitle: read.snapshot.title, run: read.snapshot.run,
|
|
816
|
-
snapshot: read.snapshot, operation, note, path, line, notes });
|
|
841
|
+
snapshot: read.snapshot, operation, note, path, line, notes, image });
|
|
817
842
|
return id === null ? tooMany() : { ok: true, body: { proposal: id, kind: "review", awaiting: "confirmation", operation } };
|
|
818
843
|
},
|
|
819
844
|
},
|
|
@@ -1497,7 +1522,8 @@ const LEAD_TOOL_HANDLERS = {
|
|
|
1497
1522
|
const integrations = list.map(one => ({
|
|
1498
1523
|
name: one.name, kind: one.group, state: STATE_WORDS[one.state], account: one.account, detail: one.detail, usedBy: one.usedBy,
|
|
1499
1524
|
lastSuccessAt: one.lastSuccessAt, lastError: one.lastError,
|
|
1500
|
-
next: one.action.kind === "setup" ? "Not set up: open Settings → Integrations to set it up" : one.action.kind === "fix" ? `Fix: ${one.action.words}`
|
|
1525
|
+
next: one.action.kind === "setup" ? "Not set up: open Settings → Integrations to set it up" : one.action.kind === "fix" ? `Fix: ${one.action.words}`
|
|
1526
|
+
: one.action.kind === "switch" ? (one.action.words !== null ? `Fix: ${one.action.words}` : one.action.on ? "Turned off: turn it on in Settings → Integrations" : null) : null,
|
|
1501
1527
|
}));
|
|
1502
1528
|
return { ok: true, body: { integrations, settings: "show_control integrations opens Settings → Integrations; never invent a link" } };
|
|
1503
1529
|
},
|
package/dist/lead.d.ts
CHANGED
|
@@ -38,6 +38,8 @@ export type LeadTurnInput = {
|
|
|
38
38
|
* visible thread so a task-scoped composer still reads like a normal
|
|
39
39
|
* conversation. */
|
|
40
40
|
context?: string;
|
|
41
|
+
/** Exact image from the transport record of the message being answered. */
|
|
42
|
+
replyImage?: import("./result-review.js").FeedbackImage;
|
|
41
43
|
fetcher?: typeof fetch;
|
|
42
44
|
/** Injected by tests; production invokes the isolated local harness. */
|
|
43
45
|
subscriptionRunner?: SubscriptionLeadRunner;
|
package/dist/lead.js
CHANGED
|
@@ -110,7 +110,7 @@ function settleBy(work, ms) {
|
|
|
110
110
|
const late = new Promise(resolve => { timer = setTimeout(() => resolve(LATE), Math.max(0, ms)); });
|
|
111
111
|
return Promise.race([work, late]).finally(() => clearTimeout(timer));
|
|
112
112
|
}
|
|
113
|
-
const READ_TOOLS = new Set(["get_brief", "get_project_context", "get_actions", "get_action_status", "get_skills", "get_acceptance_evidence", "recap", "list_repos", "list_tasks", "get_task", "get_result", "get_result_images", "get_controls", "get_agents", "get_project_knowledge", "get_task_conversation", "get_diff", "get_check_log", "get_project_tools", "get_flows", "get_flow_insights", "list_decisions", "get_decision", "queue"]);
|
|
113
|
+
const READ_TOOLS = new Set(["get_brief", "get_project_context", "get_project_activity", "get_actions", "get_action_status", "get_skills", "get_acceptance_evidence", "recap", "list_repos", "list_tasks", "get_task", "get_result", "get_result_images", "get_controls", "get_agents", "get_project_knowledge", "get_task_conversation", "get_diff", "get_check_log", "get_project_tools", "get_flows", "get_flow_insights", "list_decisions", "get_decision", "queue"]);
|
|
114
114
|
/** The last messages of the thread as provider-neutral history, newest kept first until the byte cap. */
|
|
115
115
|
export function historyFor(store, thread, queuedMessageId, named = queuedMessageId !== undefined) {
|
|
116
116
|
// Admitted messages only (v128): a queued message — this one, a teammate's, or one removed while waiting — never
|
|
@@ -205,7 +205,7 @@ export async function runLeadTurn(input) {
|
|
|
205
205
|
return refuse("thread-closed");
|
|
206
206
|
if (liveSession.ceilingDigest !== who.ceilingDigest || liveThread.ceilingDigest !== who.ceilingDigest)
|
|
207
207
|
return refuse("ceiling-changed");
|
|
208
|
-
const digest = createHash("sha256").update(JSON.stringify([thread.id, message, input.context ?? null])).digest("hex");
|
|
208
|
+
const digest = createHash("sha256").update(JSON.stringify([thread.id, message, input.context ?? null, ...(input.replyImage === undefined ? [] : [input.replyImage])])).digest("hex");
|
|
209
209
|
const receipt = request === undefined ? null : store.leadRequestReceipt(session.id, request);
|
|
210
210
|
if (receipt !== null)
|
|
211
211
|
return receipt.digest === digest ? { ok: true, replayed: true, turn: receipt.turn } : refuse("request-changed");
|
|
@@ -568,7 +568,7 @@ export async function runLeadTurn(input) {
|
|
|
568
568
|
// caller of executeLeadTool): the model learns the road (find_tools, then the next step) and the reply goes on.
|
|
569
569
|
const outcome = !advertisedNames.has(call.name)
|
|
570
570
|
? { ok: false, message: `${call.name} isn't available in this step. Call find_tools with what you want to do; tools it finds can be called from your next step.` }
|
|
571
|
-
: executeLeadTool({ store, who, now: clock(), draft, selectEvidence, step: steps, readDecisions, readResults, searchedMemory, checkedCapabilities, ask, findTools, ...(input.integrations === undefined ? {} : { integrations: input.integrations }), thread: thread.id, turn: turnId, ...(promiseChannelOfTurn(input.channel) === undefined ? {} : { channel: promiseChannelOfTurn(input.channel) }), ...(input.evidenceRoot === undefined ? {} : { evidenceRoot: input.evidenceRoot }), ...(input.mediaDelivery === undefined ? {} : { mediaDelivery: input.mediaDelivery }) }, call.name, call.args, view);
|
|
571
|
+
: executeLeadTool({ store, who, now: clock(), draft, selectEvidence, ...(input.replyImage === undefined ? {} : { replyImage: input.replyImage }), step: steps, readDecisions, readResults, searchedMemory, checkedCapabilities, ask, findTools, ...(input.integrations === undefined ? {} : { integrations: input.integrations }), thread: thread.id, turn: turnId, ...(promiseChannelOfTurn(input.channel) === undefined ? {} : { channel: promiseChannelOfTurn(input.channel) }), ...(input.evidenceRoot === undefined ? {} : { evidenceRoot: input.evidenceRoot }), ...(input.mediaDelivery === undefined ? {} : { mediaDelivery: input.mediaDelivery }) }, call.name, call.args, view);
|
|
572
572
|
let displayOutcome = { state: "succeeded" };
|
|
573
573
|
if (!outcome.ok) {
|
|
574
574
|
const reason = leadToolFailureReason(call.name, outcome.message);
|
package/dist/model-catalog.d.ts
CHANGED
|
@@ -47,6 +47,11 @@ export declare const RUNTIME_TOOLS: Record<RuntimeTool, {
|
|
|
47
47
|
pkg: string;
|
|
48
48
|
}>;
|
|
49
49
|
export declare const WATCH_EVERY_MS: number;
|
|
50
|
+
/** The current Claude models by exact id, offered even before the first catalog check has run. */
|
|
51
|
+
export declare const CLAUDE_EXACT: readonly {
|
|
52
|
+
value: string;
|
|
53
|
+
label: string;
|
|
54
|
+
}[];
|
|
50
55
|
/** `anthropic/claude-opus-5.5` → `claude-opus-5-5`, the name the Claude CLI and API use. */
|
|
51
56
|
export declare function claudeIdOf(openrouterId: string): string | null;
|
|
52
57
|
/** The Codex CLI's own list — account-specific, maintained by the CLI. */
|
package/dist/model-catalog.js
CHANGED
|
@@ -58,6 +58,12 @@ export const WATCH_EVERY_MS = 6 * 3_600_000;
|
|
|
58
58
|
const NEW_FOR_MS = 14 * 86_400_000;
|
|
59
59
|
const FAMILIES = ["opus", "sonnet", "haiku", "fable"];
|
|
60
60
|
const ALIAS_WORDS = { opus: "complex tasks", sonnet: "everyday coding", haiku: "smaller tasks" };
|
|
61
|
+
/** The current Claude models by exact id, offered even before the first catalog check has run. */
|
|
62
|
+
export const CLAUDE_EXACT = Object.freeze([
|
|
63
|
+
{ value: "claude-opus-5-5", label: "Claude Opus 5.5" },
|
|
64
|
+
{ value: "claude-sonnet-5-5", label: "Claude Sonnet 5.5" },
|
|
65
|
+
{ value: "claude-haiku-5-5", label: "Claude Haiku 5.5" },
|
|
66
|
+
]);
|
|
61
67
|
const title = (word) => word.charAt(0).toUpperCase() + word.slice(1);
|
|
62
68
|
const stripVendor = (name) => name.replace(/^(Anthropic|OpenAI|Google): /, "");
|
|
63
69
|
/** `anthropic/claude-opus-5.5` → `claude-opus-5-5`, the name the Claude CLI and API use. */
|
|
@@ -182,12 +188,15 @@ export function modelWords(store, provider, model) {
|
|
|
182
188
|
export function modelOptions(store, provider, now, home = homedir()) {
|
|
183
189
|
const option = (model) => ({ value: model.id, label: `${model.name} · ${priceWords(model)}${isNewModel(model, now) ? " · New" : ""}`, isNew: isNewModel(model, now) });
|
|
184
190
|
if (provider === "claude") {
|
|
191
|
+
// Exact ids first (Settings → Models presets write only these); the short names float to the newest version.
|
|
192
|
+
const seen = seenModels(store, "claude").slice(0, 20);
|
|
193
|
+
const builtIn = CLAUDE_EXACT.filter(one => !seen.some(model => model.id === one.value)).map(one => ({ ...one, isNew: false }));
|
|
185
194
|
const aliases = ["sonnet", "opus", "haiku"].map(alias => {
|
|
186
195
|
const target = aliasTarget(store, alias);
|
|
187
196
|
// Short enough that a phone's select still shows which model it means.
|
|
188
|
-
return { value: alias, label: target === null ? `${title(alias)} — ${ALIAS_WORDS[alias]}` : `${title(alias)} ·
|
|
197
|
+
return { value: alias, label: target === null ? `${title(alias)} (floating) — ${ALIAS_WORDS[alias]}` : `${title(alias)} (floating) · now ${target.name.replace(/^Claude /, "")}`, isNew: false };
|
|
189
198
|
});
|
|
190
|
-
return [...
|
|
199
|
+
return [...seen.map(option), ...builtIn, ...aliases];
|
|
191
200
|
}
|
|
192
201
|
if (provider === "codex") {
|
|
193
202
|
const live = codexCatalog(home);
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { type SeenModel } from "./model-catalog.js";
|
|
2
|
+
import { type PhaseRoute } from "./phase-routing.js";
|
|
3
|
+
import type { Store } from "./store.js";
|
|
4
|
+
export type SlotPhase = "plan" | "build" | "repair" | "review";
|
|
5
|
+
export type SlotTier = "routine" | "light" | "strong";
|
|
6
|
+
export type Slot = {
|
|
7
|
+
phase: SlotPhase;
|
|
8
|
+
tier: SlotTier;
|
|
9
|
+
};
|
|
10
|
+
export type Pair = {
|
|
11
|
+
provider: string;
|
|
12
|
+
model: string | null;
|
|
13
|
+
};
|
|
14
|
+
/** One row to set (an exact pair) or clear (null). */
|
|
15
|
+
/** A model is null only on the command line's `config set <phase> --provider p` (its harness default). */
|
|
16
|
+
export type ModelChange = {
|
|
17
|
+
slot: Slot;
|
|
18
|
+
to: {
|
|
19
|
+
provider: string;
|
|
20
|
+
model: string | null;
|
|
21
|
+
} | null;
|
|
22
|
+
};
|
|
23
|
+
/** Every row the editor owns, in the order the page and the diff show them. Alternates (`--also`) and the strong
|
|
24
|
+
* repair row stay CLI-only; presets clear the strong repair row so repairs follow the builder. */
|
|
25
|
+
export declare const SLOTS: readonly Slot[];
|
|
26
|
+
/** Every row the write path accepts: the editor's, plus the strong repair row the CLI also sets. */
|
|
27
|
+
export declare const ALL_SLOTS: readonly Slot[];
|
|
28
|
+
export declare const slotKey: (slot: Slot) => string;
|
|
29
|
+
export declare function slotOf(key: string): Slot | null;
|
|
30
|
+
/** The role in plain words. */
|
|
31
|
+
export declare function slotWords(slot: Slot): string;
|
|
32
|
+
export declare function rowAt(store: Store, scope: string, slot: Slot): Pair | null;
|
|
33
|
+
/** Every value the editor shows for a scope: its own rows and, for a project, the installation rows it inherits, and
|
|
34
|
+
* the alternates described in words. A change to any of them makes an open form stale. */
|
|
35
|
+
export declare function stateDigest(store: Store, scope: string): string;
|
|
36
|
+
/** The exact change a preview showed: its target and every set and clear, in slot order. */
|
|
37
|
+
export declare function changeDigest(scope: string, change: readonly ModelChange[]): string;
|
|
38
|
+
export type DiffRow = {
|
|
39
|
+
slot: Slot;
|
|
40
|
+
before: Pair | null;
|
|
41
|
+
after: Pair | null;
|
|
42
|
+
};
|
|
43
|
+
/** What a preview showed: the scope and every row's before → after. Bound into the apply, so a row that moved in
|
|
44
|
+
* between, or a change the preview never showed, is refused. */
|
|
45
|
+
export declare function diffDigest(scope: string, diff: readonly DiffRow[]): string;
|
|
46
|
+
/** A preview read from ONE snapshot: the rows it moves, what a cleared project row would inherit, and both digests
|
|
47
|
+
* the apply must match. */
|
|
48
|
+
export declare function previewModelChange(store: Store, scope: string, change: readonly ModelChange[]): {
|
|
49
|
+
rows: (DiffRow & {
|
|
50
|
+
inherited: Pair | null;
|
|
51
|
+
})[];
|
|
52
|
+
state: string;
|
|
53
|
+
change: string;
|
|
54
|
+
};
|
|
55
|
+
/** The rows this change really moves (a set to the value already there is no change). */
|
|
56
|
+
export declare function diffOf(store: Store, scope: string, change: readonly ModelChange[]): DiffRow[];
|
|
57
|
+
export declare const OPUS = "claude-opus-5-5";
|
|
58
|
+
export declare const SONNET = "claude-sonnet-5-5";
|
|
59
|
+
export declare const HAIKU = "claude-haiku-5-5";
|
|
60
|
+
export type PresetName = "sized" | "opus";
|
|
61
|
+
export declare const PRESETS: Record<PresetName, {
|
|
62
|
+
label: string;
|
|
63
|
+
words: string;
|
|
64
|
+
rows: Record<string, string | null>;
|
|
65
|
+
}>;
|
|
66
|
+
export declare const DEFAULT_PRESET: PresetName;
|
|
67
|
+
export declare const isPresetName: (value: string | null) => value is PresetName;
|
|
68
|
+
/** A preset as a change: every slot, as exact Claude ids, never an alias. Repair rows are cleared so repairs follow
|
|
69
|
+
* the builder of each size. */
|
|
70
|
+
export declare function presetChange(name: PresetName): ModelChange[];
|
|
71
|
+
/** The routes a task filed now would get at a scope, for every size and demand that picks a tier (inheritance,
|
|
72
|
+
* review following the planner, and repair following each size's builder included), or the words for why none can. */
|
|
73
|
+
export declare const ROUTE_CASES: readonly {
|
|
74
|
+
label: string;
|
|
75
|
+
size: "small" | "medium" | "large";
|
|
76
|
+
risky: boolean;
|
|
77
|
+
strict: boolean;
|
|
78
|
+
}[];
|
|
79
|
+
export declare function effectiveRoutes(store: Store, repo: string | null): {
|
|
80
|
+
ok: true;
|
|
81
|
+
routes: {
|
|
82
|
+
label: string;
|
|
83
|
+
route: PhaseRoute;
|
|
84
|
+
}[];
|
|
85
|
+
} | {
|
|
86
|
+
ok: false;
|
|
87
|
+
problem: string;
|
|
88
|
+
};
|
|
89
|
+
/** Which preset a scope's EFFECTIVE configuration matches (what each size would really run, inherited rows
|
|
90
|
+
* included), or null ("a custom set"). */
|
|
91
|
+
export declare function presetInUse(store: Store, scope: string): PresetName | null;
|
|
92
|
+
/** "Switch to it": every row at this scope that names `from` names `to` instead. */
|
|
93
|
+
export declare function switchChange(store: Store, scope: string, from: string, to: string): ModelChange[];
|
|
94
|
+
/** For each exact Claude id this scope uses, the newest model of its family the watch has seen, when that is newer. */
|
|
95
|
+
export declare function newerModels(store: Store, scope: string): {
|
|
96
|
+
from: string;
|
|
97
|
+
to: SeenModel;
|
|
98
|
+
slots: Slot[];
|
|
99
|
+
}[];
|
|
100
|
+
/** Who is acting, proved by the caller's own step-up, and re-proved here inside the transaction: the same account,
|
|
101
|
+
* still active, still an unrestricted instance operator, at the credential generation the proof was made under, on a
|
|
102
|
+
* session (or local CLI login) that is still live. */
|
|
103
|
+
export type ModelAuthority = {
|
|
104
|
+
actor: string;
|
|
105
|
+
/** The credential generation the proof was checked against (read before the proof, never after). */
|
|
106
|
+
generation: number;
|
|
107
|
+
/** How the person proved it (scope.ts AuthMethod): a Passwordless session or remembered CLI login stands only while
|
|
108
|
+
* Passwordless is still on, and is re-checked here. */
|
|
109
|
+
method: string;
|
|
110
|
+
live: () => boolean;
|
|
111
|
+
};
|
|
112
|
+
export type ApplyInput = {
|
|
113
|
+
scope: string;
|
|
114
|
+
change: readonly ModelChange[];
|
|
115
|
+
/** The state a page showed, and (for a preview) the exact change it showed; null on the command line, which shows
|
|
116
|
+
* nothing before it writes. */
|
|
117
|
+
expected: {
|
|
118
|
+
state: string;
|
|
119
|
+
change?: string;
|
|
120
|
+
} | null;
|
|
121
|
+
/** The console: exact ids only. The CLI may also name a floating name, or a routine row with no model. The
|
|
122
|
+
* resulting configuration is checked the same way for both. */
|
|
123
|
+
strict: boolean;
|
|
124
|
+
knownProject: (repo: string) => boolean;
|
|
125
|
+
/** The command line's other rows, under the same proof, target and result checks: another provider's candidate on
|
|
126
|
+
* a tier (`--also`), and removing a light planner left from before light became build-only. */
|
|
127
|
+
extra?: readonly ExtraWrite[];
|
|
128
|
+
};
|
|
129
|
+
export type ExtraWrite = {
|
|
130
|
+
kind: "also-set";
|
|
131
|
+
phase: "plan" | "build";
|
|
132
|
+
tier: SlotTier;
|
|
133
|
+
provider: string;
|
|
134
|
+
model: string;
|
|
135
|
+
} | {
|
|
136
|
+
kind: "also-clear";
|
|
137
|
+
phase: "plan" | "build";
|
|
138
|
+
tier: SlotTier;
|
|
139
|
+
provider: string | null;
|
|
140
|
+
} | {
|
|
141
|
+
kind: "legacy-light-clear";
|
|
142
|
+
phase: SlotPhase;
|
|
143
|
+
};
|
|
144
|
+
export type ApplyRefusal = {
|
|
145
|
+
ok: false;
|
|
146
|
+
reason: "authority" | "unknown-project" | "stale" | "invalid";
|
|
147
|
+
problem: string;
|
|
148
|
+
};
|
|
149
|
+
export type ApplyResult = {
|
|
150
|
+
ok: true;
|
|
151
|
+
diff: DiffRow[];
|
|
152
|
+
unresolved: string | null;
|
|
153
|
+
extra: number;
|
|
154
|
+
} | ApplyRefusal;
|
|
155
|
+
export type ScopeCheck = {
|
|
156
|
+
kind: "ok";
|
|
157
|
+
} | {
|
|
158
|
+
kind: "incomplete" | "invalid";
|
|
159
|
+
problem: string;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Whether tasks can be filed at a scope with its resulting configuration. INCOMPLETE: a role has no exact model yet
|
|
163
|
+
* (setup in progress; saved, and said). INVALID: something set can't run: an unknown provider, an ineligible reviewer,
|
|
164
|
+
* or a route of any size or demand (small, routine, large, risky, strict) whose repair can't resume its builder,
|
|
165
|
+
* inherited rows included. An invalid result is never saved.
|
|
166
|
+
*/
|
|
167
|
+
export declare function checkScope(store: Store, repo: string | null): ScopeCheck;
|
|
168
|
+
/** A resolver's words for a browser: what is wrong, never a terminal command to fix it. */
|
|
169
|
+
export declare function browserWords(problem: string): string;
|
|
170
|
+
/** The problem alone, or null when tasks can be filed. */
|
|
171
|
+
export declare function scopeProblem(store: Store, repo: string | null): string | null;
|
|
172
|
+
/**
|
|
173
|
+
* Apply one change in ONE transaction: re-prove the actor, check the target and that nothing moved since the person
|
|
174
|
+
* looked, check every row, write every set and clear, check the resulting configuration (no scope it reaches, the
|
|
175
|
+
* target or a project inheriting from it, may stop resolving), and ledger before → after for each row. Any refusal
|
|
176
|
+
* rolls everything back: a preset that fails on its last row writes nothing.
|
|
177
|
+
*/
|
|
178
|
+
export declare function applyModelChoices(store: Store, input: ApplyInput, authority: ModelAuthority, now: Date): ApplyResult;
|
|
179
|
+
export type RecentRun = {
|
|
180
|
+
runId: number;
|
|
181
|
+
taskId: string;
|
|
182
|
+
role: string;
|
|
183
|
+
chosen: string | null;
|
|
184
|
+
ranOn: string | null;
|
|
185
|
+
at: string;
|
|
186
|
+
};
|
|
187
|
+
/** The latest runs' floating or exact choice next to the model their provider reported: no verdict. */
|
|
188
|
+
export declare function recentRuns(store: Store, limit?: number): RecentRun[];
|