toolroll 0.9.51 → 0.9.53
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 +20 -0
- package/README.md +55 -80
- package/dist/agentconfig.d.ts +11 -36
- package/dist/agentconfig.js +3 -62
- package/dist/approval-policy.d.ts +1 -1
- package/dist/approval-policy.js +1 -1
- package/dist/backup.d.ts +8 -0
- package/dist/backup.js +10 -1
- package/dist/board.d.ts +3 -19
- package/dist/board.js +4 -42
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +18 -18
- package/dist/browser-workspace.d.ts +3 -1
- package/dist/builder.d.ts +9 -40
- package/dist/builder.js +50 -257
- package/dist/chat-actions.js +3 -14
- package/dist/chat-controls.d.ts +0 -4
- package/dist/chat-controls.js +0 -1
- package/dist/chat-decide.js +1 -3
- package/dist/chat-delivery.d.ts +0 -2
- package/dist/chat-delivery.js +0 -1
- package/dist/chat-quiet.js +2 -2
- package/dist/chat-task-actions.d.ts +1 -1
- package/dist/chat-task-actions.js +0 -2
- package/dist/claim.d.ts +79 -244
- package/dist/claim.js +688 -1023
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +10 -2
- package/dist/contracts/cli.d.ts +0 -13
- package/dist/contracts/cli.js +0 -2
- package/dist/contracts/console-api.d.ts +5 -10
- package/dist/contracts/console-api.js +4 -10
- package/dist/contracts/flow.d.ts +50 -0
- package/dist/contracts/flow.js +26 -1
- package/dist/contracts/lead-tools.d.ts +0 -13
- package/dist/contracts/lead-tools.js +3 -3
- package/dist/contracts/store-json.d.ts +1 -25
- package/dist/contracts/store-json.js +3 -13
- package/dist/converse.d.ts +1 -11
- package/dist/converse.js +6 -38
- package/dist/coordinator.d.ts +0 -1
- package/dist/coordinator.js +3 -5
- package/dist/database-reclaim.d.ts +4 -0
- package/dist/database-reclaim.js +13 -0
- package/dist/demo.d.ts +1 -1
- package/dist/demo.js +17 -27
- package/dist/desktop-update-gate.js +0 -1
- package/dist/desktop-update.d.ts +1 -1
- package/dist/desktop-update.js +1 -3
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +1 -1
- package/dist/dispose.d.ts +2 -3
- package/dist/dispose.js +17 -92
- package/dist/envelope.js +0 -3
- package/dist/exec.d.ts +0 -64
- package/dist/exec.js +3 -356
- package/dist/exhaustion.d.ts +22 -76
- package/dist/exhaustion.js +14 -140
- package/dist/export.js +4 -4
- package/dist/flow-engine.js +1 -1
- package/dist/flow-schedule.d.ts +154 -0
- package/dist/flow-schedule.js +321 -0
- package/dist/flow-share.js +1 -1
- package/dist/flow-triggers.d.ts +6 -1
- package/dist/flow-triggers.js +65 -10
- package/dist/flows-cli.js +8 -4
- package/dist/flows-ui.js +2 -0
- package/dist/flows.js +1 -1
- package/dist/guides.js +9 -29
- package/dist/invoke.d.ts +1 -24
- package/dist/invoke.js +17 -259
- package/dist/lead-status.js +2 -4
- package/dist/lead-voice.js +1 -1
- package/dist/live-bus.d.ts +3 -3
- package/dist/live-bus.js +3 -3
- package/dist/mate-doors.d.ts +1 -2
- package/dist/mate-doors.js +12 -25
- package/dist/mate-tools.d.ts +2 -2
- package/dist/mate-tools.js +10 -22
- package/dist/modes.d.ts +6 -7
- package/dist/modes.js +7 -13
- package/dist/monitoring.js +32 -8
- package/dist/notification-delivery.d.ts +19 -0
- package/dist/notification-delivery.js +37 -0
- package/dist/operate-remote-arguments.js +2 -6
- package/dist/operate-remote.js +6 -7
- package/dist/operate.d.ts +0 -6
- package/dist/operate.js +283 -2378
- package/dist/phase-routing.d.ts +13 -23
- package/dist/phase-routing.js +13 -35
- package/dist/plan-auto.js +2 -2
- package/dist/planner-source.js +2 -1
- package/dist/policy.d.ts +1 -4
- package/dist/policy.js +0 -8
- package/dist/process-recovery-eligibility.js +2 -3
- package/dist/process-recovery-settlement.js +1 -0
- package/dist/project-delete.js +3 -15
- package/dist/proposal.d.ts +4 -24
- package/dist/proposal.js +3 -88
- package/dist/provider-auth.d.ts +3 -3
- package/dist/provider-auth.js +4 -6
- package/dist/provider.d.ts +11 -25
- package/dist/provider.js +4 -36
- package/dist/push.js +1 -1
- package/dist/recipe-ui.js +6 -6
- package/dist/recipes.d.ts +3 -3
- package/dist/recipes.js +27 -23
- package/dist/retention.js +6 -5
- package/dist/run-closeout.d.ts +1 -1
- package/dist/run-closeout.js +1 -4
- package/dist/scope.d.ts +11 -65
- package/dist/scope.js +17 -82
- package/dist/serve.js +16 -25
- package/dist/server/chat.js +7 -30
- package/dist/server/flows.js +11 -10
- package/dist/server/guards.js +0 -1
- package/dist/server/pages-assets.js +1 -1
- package/dist/server/pages.js +0 -2
- package/dist/server/people-tokens.js +0 -4
- package/dist/server/route-table.d.ts +1 -1
- package/dist/server/route-table.js +2 -17
- package/dist/server/settings.js +4 -9
- package/dist/server/shared.d.ts +29 -212
- package/dist/server/shared.js +73 -780
- package/dist/server/task-views.js +9 -76
- package/dist/server/tasks.d.ts +1 -50
- package/dist/server/tasks.js +36 -946
- package/dist/setup-guide.d.ts +2 -2
- package/dist/slack-chat.d.ts +0 -2
- package/dist/slack-chat.js +0 -1
- package/dist/store.d.ts +142 -1382
- package/dist/store.js +900 -4083
- package/dist/surface.js +10 -25
- package/dist/task-composer.d.ts +1 -1
- package/dist/task-composer.js +1 -1
- package/dist/task-control.d.ts +3 -10
- package/dist/task-control.js +2 -7
- package/dist/teammate-desk.js +1 -1
- package/dist/teammates-ui.js +1 -1
- package/dist/telegram-mate.d.ts +0 -3
- package/dist/telegram-mate.js +0 -1
- package/dist/templates.d.ts +11 -7
- package/dist/templates.js +12 -9
- package/dist/toolroll-update.d.ts +24 -3
- package/dist/toolroll-update.js +159 -16
- package/dist/webhooks.d.ts +10 -22
- package/dist/webhooks.js +43 -81
- package/dist/work-index.js +3 -7
- package/dist/workspace-revision.d.ts +28 -1
- package/dist/workspace-revision.js +276 -49
- package/dist/workspace-ui.js +1 -1
- package/package.json +1 -3
- package/scripts/recovery-canary.mjs +3 -3
- package/dist/contest.d.ts +0 -318
- package/dist/contest.js +0 -753
- package/dist/held.d.ts +0 -150
- package/dist/held.js +0 -799
- package/dist/liveness.d.ts +0 -30
- package/dist/liveness.js +0 -42
- package/dist/pricing.d.ts +0 -45
- package/dist/pricing.js +0 -77
- package/dist/routine.d.ts +0 -228
- package/dist/routine.js +0 -764
- package/dist/supervisor.mjs +0 -297
package/dist/mate-doors.js
CHANGED
|
@@ -23,7 +23,7 @@ import { readProjectAccess } from "./project-access.js";
|
|
|
23
23
|
import { TeamLeads } from './team-leads.js';
|
|
24
24
|
import { fileTaskProposal } from "./proposal.js";
|
|
25
25
|
import { proposeGuarded } from "./scope.js";
|
|
26
|
-
import {
|
|
26
|
+
import { isTaskSize, PHASES, specWords } from "./phase-routing.js";
|
|
27
27
|
import { isProviderId } from "./provider.js";
|
|
28
28
|
import { applyChatReview } from "./chat-review.js";
|
|
29
29
|
import { applyChatTaskAction } from "./chat-task-actions.js";
|
|
@@ -340,9 +340,9 @@ function executeProposal(store, actor, kind, payload, now, options) {
|
|
|
340
340
|
if (ref === null || !admitted(ref.repo))
|
|
341
341
|
return refuse("unknown-task", "no such task in your projects");
|
|
342
342
|
if (kind === "task_action") {
|
|
343
|
-
//
|
|
343
|
+
// A person's own surface (the console, the paired phone) honours the
|
|
344
344
|
// operator's own automatic-approval mode; the CLI keeps its ceremony.
|
|
345
|
-
const result = applyChatTaskAction(store, actor, payload, now, options.via !== "cli", {
|
|
345
|
+
const result = applyChatTaskAction(store, actor, payload, now, options.via !== "cli", { deferSignal: options.deferSignal, via: options.via, ...(options.evidenceRoot === undefined ? {} : { evidenceRoot: options.evidenceRoot }) });
|
|
346
346
|
return result.ok ? { ok: true, kind, taskId: result.taskId, said: result.said } : refuse("stale", result.message);
|
|
347
347
|
}
|
|
348
348
|
if (kind === "review") {
|
|
@@ -410,14 +410,12 @@ function executeProposal(store, actor, kind, payload, now, options) {
|
|
|
410
410
|
return refuse("refused", "this proposal carries no guidance");
|
|
411
411
|
const filed = store.fileSteerNote(taskId, verifiedAuthor(actor.name), note, now);
|
|
412
412
|
if (!filed.ok) {
|
|
413
|
-
const said = filed.reason === "
|
|
414
|
-
? "
|
|
415
|
-
: filed.reason === "
|
|
416
|
-
?
|
|
417
|
-
:
|
|
418
|
-
|
|
419
|
-
: "no such task";
|
|
420
|
-
return refuse(filed.reason === "contest-open" ? "contest-open" : "refused", said);
|
|
413
|
+
const said = filed.reason === "task-finished"
|
|
414
|
+
? "this task is finished — guidance has no next attempt to reach"
|
|
415
|
+
: filed.reason === "invalid-note"
|
|
416
|
+
? (filed.problem ?? "that guidance could not be saved")
|
|
417
|
+
: "no such task";
|
|
418
|
+
return refuse("refused", said);
|
|
421
419
|
}
|
|
422
420
|
const taskName = payloadString(payload, "taskTitle") ?? taskId;
|
|
423
421
|
return { ok: true, kind, said: `Guidance saved for ${taskName}'s next attempt`, taskId };
|
|
@@ -437,9 +435,6 @@ function executeProposal(store, actor, kind, payload, now, options) {
|
|
|
437
435
|
(blockerTask.state !== "failed" && blockerTask.state !== "cancelled")) {
|
|
438
436
|
return refuse("stale", "the task it was waiting for changed since this was proposed — look again");
|
|
439
437
|
}
|
|
440
|
-
if (store.openContestFor(ref.id) !== null) {
|
|
441
|
-
return refuse("contest-open", "a tournament is running on the dependent task — let it finish first");
|
|
442
|
-
}
|
|
443
438
|
if (operation === "retry") {
|
|
444
439
|
const blockerRef = store.lookupRef(blocker);
|
|
445
440
|
if (blockerTask.state !== "failed" || blockerRef === null || !admitted(blockerRef.repo)) {
|
|
@@ -480,25 +475,22 @@ function executeProposal(store, actor, kind, payload, now, options) {
|
|
|
480
475
|
// authenticated route-edit transaction — the same door the task page
|
|
481
476
|
// uses. Re-proved here, inside it: the actor's standing (the edit's
|
|
482
477
|
// own authenticate hook), the digest the card was drafted against
|
|
483
|
-
// (CAS — a scope rewritten meanwhile refuses), a live claim
|
|
484
|
-
//
|
|
478
|
+
// (CAS — a scope rewritten meanwhile refuses), a live claim (refused
|
|
479
|
+
// inside the store), and — for an agent — that the
|
|
485
480
|
// pair is STILL one of the configured, role-valid choices right now.
|
|
486
481
|
// Nothing the mate wrote becomes authority: only a configured pair,
|
|
487
482
|
// recorded under the operator's name, ever reaches the route.
|
|
488
|
-
const risk = payload["risk"];
|
|
489
483
|
const phase = payload["phase"];
|
|
490
484
|
const clear = payload["clear"] === true;
|
|
491
485
|
const provider = payloadString(payload, "provider");
|
|
492
486
|
const model = payloadString(payload, "model");
|
|
493
487
|
const size = payload["size"];
|
|
494
488
|
const risky = payload["risky"];
|
|
495
|
-
if (risk !== undefined && !isRiskLevel(risk))
|
|
496
|
-
return refuse("refused", "this proposal carries an unknown risk level");
|
|
497
489
|
if (size !== undefined && (!isTaskSize(size) || typeof risky !== "boolean"))
|
|
498
490
|
return refuse("refused", "this proposal carries an unknown size");
|
|
499
491
|
if (phase !== undefined && (typeof phase !== "string" || !PHASES.includes(phase)))
|
|
500
492
|
return refuse("refused", "this proposal names an unknown role");
|
|
501
|
-
if (
|
|
493
|
+
if (phase === undefined && size === undefined)
|
|
502
494
|
return refuse("refused", "this proposal changes nothing about the agents");
|
|
503
495
|
let override;
|
|
504
496
|
if (typeof phase === "string") {
|
|
@@ -517,7 +509,6 @@ function executeProposal(store, actor, kind, payload, now, options) {
|
|
|
517
509
|
const standing = reproveApprover(store, actor);
|
|
518
510
|
return standing.ok ? { ok: true } : { ok: false, reason: `your approver standing changed (${standing.reason}) — sign in again` };
|
|
519
511
|
},
|
|
520
|
-
...(isRiskLevel(risk) ? { risk } : {}),
|
|
521
512
|
...(isTaskSize(size) && typeof risky === "boolean" ? { size: { size, risky } } : {}),
|
|
522
513
|
...(override === undefined ? {} : { override }),
|
|
523
514
|
expectDigest: payloadString(payload, "sawDigest"),
|
|
@@ -533,8 +524,6 @@ function executeProposal(store, actor, kind, payload, now, options) {
|
|
|
533
524
|
return refuse("stale", "the scope was rewritten since this was proposed — look again");
|
|
534
525
|
if (edited.reason === "live-claim")
|
|
535
526
|
return refuse("claimed", "this task is being built right now");
|
|
536
|
-
if (edited.reason === "contest-open")
|
|
537
|
-
return refuse("contest-open", "a tournament is running on this task — let it finish first");
|
|
538
527
|
if (edited.reason === "unauthenticated")
|
|
539
528
|
return refuse("standing", edited.detail);
|
|
540
529
|
if (edited.reason === "no-task")
|
|
@@ -548,7 +537,6 @@ function executeProposal(store, actor, kind, payload, now, options) {
|
|
|
548
537
|
const taskName = payloadString(payload, "taskTitle") ?? taskId;
|
|
549
538
|
const roleWord = typeof phase === "string" ? { plan: "planner", build: "builder", repair: "repair", review: "reviewer" }[phase] ?? phase : null;
|
|
550
539
|
const changed = [
|
|
551
|
-
...(isRiskLevel(risk) ? [`risk is now ${riskTitle(risk).toLowerCase()}`] : []),
|
|
552
540
|
...(isTaskSize(size) ? [`it is now a ${size}${risky === true ? ", risky" : ""} change`] : []),
|
|
553
541
|
...(roleWord === null ? [] : clear ? [`the ${roleWord} choice was cleared — the recommendation stands again`] : [`the ${roleWord} is now ${specWords({ provider: provider, model: model })}`]),
|
|
554
542
|
];
|
|
@@ -586,7 +574,6 @@ function refuseFromReason(kind, reason) {
|
|
|
586
574
|
changed: { reason: "stale", said: "the scope was rewritten since this was proposed — look again" },
|
|
587
575
|
claimed: { reason: "claimed", said: "this task is being built right now" },
|
|
588
576
|
"not-queued": { reason: "not-queued", said: "only queued work can move — this task is not waiting in the queue" },
|
|
589
|
-
"contest-open": { reason: "contest-open", said: "a tournament is running on this task — let it finish first" },
|
|
590
577
|
"unknown-task": { reason: "unknown-task", said: "no such task" },
|
|
591
578
|
};
|
|
592
579
|
const typed = known[reason];
|
package/dist/mate-tools.d.ts
CHANGED
|
@@ -96,8 +96,8 @@ export declare function decisionOver(store: Store, repos: readonly string[], dec
|
|
|
96
96
|
export declare function queueOver(store: Store, repo: string, now: Date): Record<string, unknown>;
|
|
97
97
|
export declare const ISO_STAMP_RULE: RegExp;
|
|
98
98
|
/**
|
|
99
|
-
* THE AGENTS A TASK RUNS UNDER, as chat reads them (v48): the
|
|
100
|
-
*
|
|
99
|
+
* THE AGENTS A TASK RUNS UNDER, as chat reads them (v48): the task's size,
|
|
100
|
+
* the same one-line summary the task page and CLI
|
|
101
101
|
* print, each role's exact agent with its reasons, the standing of those
|
|
102
102
|
* agents (approved / awaiting approval / recommended), and — for a
|
|
103
103
|
* proposal — only the configured, role-valid choices an operator could
|
package/dist/mate-tools.js
CHANGED
|
@@ -43,7 +43,7 @@ import { parseAcceptanceCriteria } from "./scope.js";
|
|
|
43
43
|
import { diagnoseTaskDispatch, withDispatchDiagnoses } from "./dispatch.js";
|
|
44
44
|
import { agentChoicesFor, routeOfTask, INSTALLATION_SCOPE } from "./agentconfig.js";
|
|
45
45
|
import { isNewModel, modelWords, priceWords, runtimeStates, seenModels } from "./model-catalog.js";
|
|
46
|
-
import { agentsSummary, chosenWords, PHASES, postureWords,
|
|
46
|
+
import { agentsSummary, chosenWords, PHASES, postureWords, routeProblems, sameSpec, sizeSourceWords, sizeWords, specWords } from "./phase-routing.js";
|
|
47
47
|
import { TOOL_CATALOG, discoverTools, projectToolsOf, secretsSetFor, toolCommandLine, toolStanding } from "./project-tools.js";
|
|
48
48
|
import { deciderOf, durationWords, FLOW_KIND_WORDS, FLOW_TEMPLATES, flowFromSteps, stepsFor, withKeptSteps } from "./flows.js";
|
|
49
49
|
import { parseContract, toModelSchema } from "./contracts/contract.js";
|
|
@@ -320,8 +320,8 @@ function scopeStandingOf(store, taskId) {
|
|
|
320
320
|
const ROLE_WORD = { plan: "planner", build: "builder", repair: "repair", review: "reviewer" };
|
|
321
321
|
const ROLE_OF_WORD = { planner: "plan", plan: "plan", builder: "build", build: "build", repair: "repair", reviewer: "review", review: "review" };
|
|
322
322
|
/**
|
|
323
|
-
* THE AGENTS A TASK RUNS UNDER, as chat reads them (v48): the
|
|
324
|
-
*
|
|
323
|
+
* THE AGENTS A TASK RUNS UNDER, as chat reads them (v48): the task's size,
|
|
324
|
+
* the same one-line summary the task page and CLI
|
|
325
325
|
* print, each role's exact agent with its reasons, the standing of those
|
|
326
326
|
* agents (approved / awaiting approval / recommended), and — for a
|
|
327
327
|
* proposal — only the configured, role-valid choices an operator could
|
|
@@ -334,14 +334,11 @@ export function agentsOver(store, taskId, now) {
|
|
|
334
334
|
return null;
|
|
335
335
|
const routed = routeOfTask(store, taskId, ref, now);
|
|
336
336
|
const scope = store.getScope(taskId);
|
|
337
|
-
const
|
|
338
|
-
const
|
|
339
|
-
const editableWhy = store.hasLiveClaim(ref.id, now) ? "this task is running — its agents cannot change under a live claim" : store.activeTournamentTerms(ref.id) !== null ? "tournament terms decide the agents while the contest is open" : null;
|
|
337
|
+
const editable = !store.hasLiveClaim(ref.id, now);
|
|
338
|
+
const editableWhy = store.hasLiveClaim(ref.id, now) ? "this task is running — its agents cannot change under a live claim" : null;
|
|
340
339
|
const base = {
|
|
341
340
|
task: taskId,
|
|
342
|
-
risk: { level: risk, title: riskTitle(risk), consequence: riskConsequence(risk) },
|
|
343
341
|
size: ref.sizing == null ? null : { size: ref.sizing.size, risky: ref.sizing.risky, reason: ref.sizing.reason, source: sizeSourceWords(ref.sizing.source) },
|
|
344
|
-
riskChoices: RISK_CHOICES.map(one => ({ risk: one.risk, title: one.title, consequence: one.consequence })),
|
|
345
342
|
editable,
|
|
346
343
|
editableWhy,
|
|
347
344
|
approval: scope === null ? "none" : scopeStandingOf(store, taskId),
|
|
@@ -1536,7 +1533,7 @@ const MATE_TOOL_HANDLERS = {
|
|
|
1536
1533
|
},
|
|
1537
1534
|
},
|
|
1538
1535
|
get_agents: {
|
|
1539
|
-
description: "Read current roles,
|
|
1536
|
+
description: "Read current roles, size, approval, configured alternatives and the organisation policy (allowed providers, models, tools, permission ceiling; changed only on Settings → Policy) before propose_agents.",
|
|
1540
1537
|
handle: (ctx, args) => {
|
|
1541
1538
|
const taskId = taskIdOf(args);
|
|
1542
1539
|
if (taskId === null)
|
|
@@ -1697,8 +1694,6 @@ const MATE_TOOL_HANDLERS = {
|
|
|
1697
1694
|
return { ok: false, message: `guidance is plain text up to ${LIMITS.note} characters` };
|
|
1698
1695
|
if (task.state === "done" || task.state === "cancelled")
|
|
1699
1696
|
return { ok: false, message: "that task is finished, so guidance has no next attempt to reach" };
|
|
1700
|
-
if (ctx.store.openContestFor(ref.id) !== null)
|
|
1701
|
-
return { ok: false, message: "agents are racing on that task — wait until the comparison finishes" };
|
|
1702
1697
|
const id = ctx.draft("steer", { task: taskId, taskTitle: task.title, repoId: ref.repoId, note: args["note"] });
|
|
1703
1698
|
if (id === null)
|
|
1704
1699
|
return tooMany();
|
|
@@ -1790,14 +1785,13 @@ const MATE_TOOL_HANDLERS = {
|
|
|
1790
1785
|
},
|
|
1791
1786
|
},
|
|
1792
1787
|
propose_agents: {
|
|
1793
|
-
description: "Read get_agents. Change
|
|
1788
|
+
description: "Read get_agents. Change size (small: fast model, no plan; large or risky: strongest agents), configured role model, or clear override; stales approval, refuses running work.",
|
|
1794
1789
|
handle: (ctx, args) => {
|
|
1795
1790
|
const taskId = taskIdOf(args);
|
|
1796
1791
|
const ref = taskId === null ? null : admittedRef(ctx, taskId);
|
|
1797
1792
|
const task = taskId === null ? null : ctx.store.getTask(taskId);
|
|
1798
1793
|
if (taskId === null || ref === null || task === null)
|
|
1799
1794
|
return notFound();
|
|
1800
|
-
const risk = args["risk"];
|
|
1801
1795
|
const roleWord = args["role"];
|
|
1802
1796
|
const phase = roleWord === undefined ? null : typeof roleWord === "string" ? ROLE_OF_WORD[roleWord] ?? null : null;
|
|
1803
1797
|
if (roleWord !== undefined && (phase === null || phase === "review"))
|
|
@@ -1808,8 +1802,8 @@ const MATE_TOOL_HANDLERS = {
|
|
|
1808
1802
|
const risky = args["risky"];
|
|
1809
1803
|
const sized = ctx.store.refForId(ref.id)?.sizing ?? null;
|
|
1810
1804
|
const sizeChange = size === undefined && risky === undefined ? null : { size: size ?? sized?.size ?? "medium", risky: risky ?? sized?.risky ?? false };
|
|
1811
|
-
if (
|
|
1812
|
-
return { ok: false, message: "say what changes: a
|
|
1805
|
+
if (phase === null && sizeChange === null)
|
|
1806
|
+
return { ok: false, message: "say what changes: a size, or a role with an agent (or clear: true)" };
|
|
1813
1807
|
if (phase !== null && !clear && (agent === null || typeof agent !== "object"))
|
|
1814
1808
|
return { ok: false, message: "a role change names an agent from get_agents, or clear: true" };
|
|
1815
1809
|
if (phase === null && (clear || agent !== undefined))
|
|
@@ -1818,8 +1812,6 @@ const MATE_TOOL_HANDLERS = {
|
|
|
1818
1812
|
return { ok: false, message: "why is plain text ≤400" };
|
|
1819
1813
|
if (ctx.store.hasLiveClaim(ref.id, ctx.now))
|
|
1820
1814
|
return { ok: false, message: "a worker is building that task right now — its agents cannot change under it" };
|
|
1821
|
-
if (ctx.store.activeTournamentTerms(ref.id) !== null)
|
|
1822
|
-
return { ok: false, message: "tournament terms decide this task's agents while the contest is open" };
|
|
1823
1815
|
const view = agentsOver(ctx.store, taskId, ctx.now);
|
|
1824
1816
|
if (view === null)
|
|
1825
1817
|
return notFound();
|
|
@@ -1840,19 +1832,16 @@ const MATE_TOOL_HANDLERS = {
|
|
|
1840
1832
|
}
|
|
1841
1833
|
chosen = { provider: match.provider, model: match.model };
|
|
1842
1834
|
const currentLeg = view["agents"].find(one => one.role === ROLE_WORD[phase]);
|
|
1843
|
-
if (currentLeg !== undefined && sameSpec(currentLeg, chosen)
|
|
1835
|
+
if (currentLeg !== undefined && sameSpec(currentLeg, chosen))
|
|
1844
1836
|
return { ok: false, message: `${specWords(chosen)} already runs the ${ROLE_WORD[phase]} role` };
|
|
1845
1837
|
}
|
|
1846
1838
|
if (phase !== null && clear && !(current?.routeOverrides ?? []).some(one => one.phase === phase)) {
|
|
1847
1839
|
return { ok: false, message: `nobody chose a ${ROLE_WORD[phase]} for this task by hand — there is nothing to clear` };
|
|
1848
1840
|
}
|
|
1849
|
-
if (risk !== undefined && phase === null && risk === view["risk"].level)
|
|
1850
|
-
return { ok: false, message: `this task is already declared ${riskTitle(risk).toLowerCase()}` };
|
|
1851
1841
|
const id = ctx.draft("agents", {
|
|
1852
1842
|
task: taskId,
|
|
1853
1843
|
taskTitle: task.title,
|
|
1854
1844
|
repoId: ref.repoId,
|
|
1855
|
-
...(risk === undefined ? {} : { risk, riskConsequence: riskConsequence(risk) }),
|
|
1856
1845
|
...(sizeChange === null ? {} : { size: sizeChange.size, risky: sizeChange.risky }),
|
|
1857
1846
|
...(phase === null ? {} : { phase, role: ROLE_WORD[phase] }),
|
|
1858
1847
|
...(chosen === null ? {} : { provider: chosen.provider, model: chosen.model }),
|
|
@@ -1870,7 +1859,6 @@ const MATE_TOOL_HANDLERS = {
|
|
|
1870
1859
|
proposal: id,
|
|
1871
1860
|
kind: "agents",
|
|
1872
1861
|
task: taskId,
|
|
1873
|
-
...(risk === undefined ? {} : { risk }),
|
|
1874
1862
|
...(sizeChange === null ? {} : { size: sizeChange.size, risky: sizeChange.risky }),
|
|
1875
1863
|
...(phase === null ? {} : { role: ROLE_WORD[phase], ...(chosen === null ? { clear: true } : { agent: chosen }) }),
|
|
1876
1864
|
awaiting: view["approval"] === "approved" ? "the operator's confirmation — the current approval will then need renewing" : "the operator's confirmation",
|
package/dist/modes.d.ts
CHANGED
|
@@ -27,7 +27,8 @@ export type ModeTerms = {
|
|
|
27
27
|
/** Explicit opt-in: a verified planner may approve a plan that preserves
|
|
28
28
|
* the signer's pre-authorized filed contract and execution terms exactly. */
|
|
29
29
|
planAuto: boolean;
|
|
30
|
-
/**
|
|
30
|
+
/** Historical signed field (watched sessions without a per-session
|
|
31
|
+
* password, removed in v115); new modes leave it false and nothing reads it. */
|
|
31
32
|
quickMint: boolean;
|
|
32
33
|
/** Historical signed field; new modes leave it false and no worker consumes
|
|
33
34
|
* it. Automatic review is the project's review switch (review-switch.ts),
|
|
@@ -47,15 +48,13 @@ export type ModeTerms = {
|
|
|
47
48
|
* "automerge" = the mode's signature substitutes for the per-merge
|
|
48
49
|
* human authorization, through the grant machinery only (D1/E1). */
|
|
49
50
|
publication: "notify" | "automerge";
|
|
50
|
-
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* must be an explicit, freshly-signed grant. A subscription->subscription
|
|
54
|
-
* fallback is not "paid" and needs no grant. */
|
|
51
|
+
/** Historical signed term (v30-v114: a grant for automatic paid
|
|
52
|
+
* fallback; fallback chains were removed in v115). Always false on new
|
|
53
|
+
* modes; retained for digest and audit compatibility. */
|
|
55
54
|
allowPaidFallback: boolean;
|
|
56
55
|
/** Whether this mode AUTHORIZES the bounded repair loop to draft AND
|
|
57
56
|
* auto-approve its own repair attempts (v40, evidence-review-v1) — the
|
|
58
|
-
*
|
|
57
|
+
* same precedent as the old paid-fallback grant: legacy modes default FALSE, a
|
|
59
58
|
* new authority is never inherited, only freshly signed. Without it, a
|
|
60
59
|
* short/refuted run with named unresolved criteria still gets exactly
|
|
61
60
|
* one drafted repair — it simply waits unapproved. */
|
package/dist/modes.js
CHANGED
|
@@ -24,7 +24,7 @@ export function presetTerms(name, absoluteExpiry) {
|
|
|
24
24
|
permissionDefault: "safe",
|
|
25
25
|
autoApproveFiling: false,
|
|
26
26
|
planAuto: false,
|
|
27
|
-
quickMint:
|
|
27
|
+
quickMint: false,
|
|
28
28
|
reviewAuto: false,
|
|
29
29
|
reviewRetryAuto: false,
|
|
30
30
|
perAttemptBudgetMicrousd: null,
|
|
@@ -42,7 +42,7 @@ export function presetTerms(name, absoluteExpiry) {
|
|
|
42
42
|
permissionDefault: "escalated",
|
|
43
43
|
autoApproveFiling: true,
|
|
44
44
|
planAuto: false,
|
|
45
|
-
quickMint:
|
|
45
|
+
quickMint: false,
|
|
46
46
|
reviewAuto: false,
|
|
47
47
|
reviewRetryAuto: false,
|
|
48
48
|
perAttemptBudgetMicrousd: null,
|
|
@@ -108,10 +108,8 @@ export function modeTermsFromJson(json) {
|
|
|
108
108
|
measured !== undefined &&
|
|
109
109
|
runs !== undefined &&
|
|
110
110
|
(t["publication"] === "notify" || t["publication"] === "automerge") &&
|
|
111
|
-
// allowPaidFallback:
|
|
112
|
-
//
|
|
113
|
-
// freshly-signed grant). A present value must be a strict boolean;
|
|
114
|
-
// anything else is a bad envelope, null.
|
|
111
|
+
// allowPaidFallback (historical): absent reads as false; a present
|
|
112
|
+
// value must be a strict boolean, anything else is a bad envelope.
|
|
115
113
|
(t["allowPaidFallback"] === undefined || typeof t["allowPaidFallback"] === "boolean") &&
|
|
116
114
|
// repairAuto/repairMaxAttempts: the SAME precedent, verbatim (v40). A
|
|
117
115
|
// legacy mode has NO such fields — that MUST read as false/0, never
|
|
@@ -155,11 +153,8 @@ export function modeWords(terms) {
|
|
|
155
153
|
terms.autoApproveFiling
|
|
156
154
|
? "every scope YOU file — signed-in console or credentialed CLI — is approved the moment you file it; while this mode is active, your signed-in browser session becomes a spend credential for this repository"
|
|
157
155
|
: "filings still wait for their own approval ceremony",
|
|
158
|
-
terms.quickMint
|
|
159
|
-
? "you start watched sessions without re-typing your password — the confirm screen still shows every term"
|
|
160
|
-
: "watched sessions keep the per-session password",
|
|
161
156
|
terms.planAuto
|
|
162
|
-
? "plans for your pre-authorized filings auto-approve only when the goal, exclusions, paths, acceptance criteria,
|
|
157
|
+
? "plans for your pre-authorized filings auto-approve only when the goal, exclusions, paths, acceptance criteria, budget, and agent route remain exactly unchanged; provide a goal, paths and acceptance criteria upfront; amendments and unresolved questions still wait for you"
|
|
163
158
|
: "planner-generated plans wait for your approval",
|
|
164
159
|
terms.name === "hands-off"
|
|
165
160
|
? "automatic review is on for this project unless you turn it off (`toolroll review off --repo <path>`): each finished build whose check passes (or that finishes, with checks Off) gets one read-only review by the project's review agent; a HIGH finding sends it back once as a revision filed under this mode, a HIGH on that revision comes to you, MEDIUM and LOW findings come to you as suggested follow-ups, and a review that fails or times out never holds the work — it reaches you marked not reviewed"
|
|
@@ -178,9 +173,8 @@ export function modeWords(terms) {
|
|
|
178
173
|
terms.publication === "automerge"
|
|
179
174
|
? "pull requests merge THEMSELVES when CI is seen green on the exact commit — you are told afterwards (requires a merge-capable publication grant)"
|
|
180
175
|
: "merges wait for you — even where a grant could merge on its own, while this mode is active",
|
|
181
|
-
terms.allowPaidFallback
|
|
182
|
-
? "
|
|
183
|
-
: "automatic fallback never switches to a paid API key on its own; a subscription that runs out stops and waits for you",
|
|
176
|
+
...(terms.allowPaidFallback
|
|
177
|
+
? ["a historical paid-fallback grant is retained on record but no longer used"] : []),
|
|
184
178
|
...(terms.repairAuto
|
|
185
179
|
? ["historical automatic repair grants are retained on record but no longer schedule work"] : []),
|
|
186
180
|
...(terms.chatApprove
|
package/dist/monitoring.js
CHANGED
|
@@ -24,6 +24,7 @@ export const SIGNATURE_HEADER = "x-standing-orders-signature";
|
|
|
24
24
|
const BATCH = 200;
|
|
25
25
|
const LEASE_MS = 60_000;
|
|
26
26
|
const TIMEOUT_MS = 10_000;
|
|
27
|
+
const CHECKPOINT_EVERY_MS = 60 * 60_000;
|
|
27
28
|
/** An audit event: the ledger entry and its seal, as the stream sends it. */
|
|
28
29
|
export function auditEvent(entry, instance) {
|
|
29
30
|
return {
|
|
@@ -41,15 +42,34 @@ const nanos = (at) => `${BigInt(Date.parse(at)) * 1000000n}`;
|
|
|
41
42
|
/** One pass over every destination that's set up. Returns what it sent, per destination. */
|
|
42
43
|
export async function monitoringPass(store, settings, deps) {
|
|
43
44
|
const sent = {};
|
|
45
|
+
const heads = [];
|
|
46
|
+
const audit = (delivered) => { if (delivered.head !== null)
|
|
47
|
+
heads.push(delivered.head); return delivered.count; };
|
|
44
48
|
// A new destination gets the whole audit history; traces start from now.
|
|
45
49
|
if (settings.webhook !== null)
|
|
46
|
-
sent.webhook = await deliver(store, "webhook", targetOf(settings.webhook.url), () => 0, deps, entries => postSigned(settings.webhook, entries, store, deps));
|
|
50
|
+
sent.webhook = audit(await deliver(store, "webhook", targetOf(settings.webhook.url), () => 0, deps, entries => postSigned(settings.webhook, entries, store, deps)));
|
|
47
51
|
if (settings.folder !== null)
|
|
48
|
-
sent.folder = await deliver(store, "folder", targetOf(settings.folder.path), () => 0, deps, async (entries) => { appendFolder(settings.folder.path, entries, deps.instance); return entries.length; });
|
|
52
|
+
sent.folder = audit(await deliver(store, "folder", targetOf(settings.folder.path), () => 0, deps, async (entries) => { appendFolder(settings.folder.path, entries, deps.instance); return entries.length; }));
|
|
49
53
|
if (settings.traces !== null)
|
|
50
|
-
sent.traces = await deliver(store, "traces", targetOf(settings.traces.endpoint), () => store.ledgerHeadId(), deps, entries => postTraces(settings.traces, entries, store, deps));
|
|
54
|
+
sent.traces = (await deliver(store, "traces", targetOf(settings.traces.endpoint), () => store.ledgerHeadId(), deps, entries => postTraces(settings.traces, entries, store, deps))).count;
|
|
55
|
+
checkpointSent(store, heads, (deps.now ?? (() => new Date()))());
|
|
51
56
|
return sent;
|
|
52
57
|
}
|
|
58
|
+
/** The furthest head the audit stream delivered, kept as an automatic checkpoint once the whole chain verifies.
|
|
59
|
+
* Automatic checkpoints write no ledger entry, so recording one gives the stream nothing new to send (and checkpoint)
|
|
60
|
+
* next pass. At most one an hour, so a busy stream neither floods the table nor walks the whole chain every pass. */
|
|
61
|
+
function checkpointSent(store, heads, now) {
|
|
62
|
+
const head = heads.reduce((best, one) => best === null || (one !== null && one.through > best.through) ? one : best, null);
|
|
63
|
+
if (head === null)
|
|
64
|
+
return;
|
|
65
|
+
const newest = store.ledgerCheckpoints(1)[0];
|
|
66
|
+
if (newest !== undefined && (newest.through >= head.through || now.getTime() - Date.parse(newest.at) < CHECKPOINT_EVERY_MS))
|
|
67
|
+
return;
|
|
68
|
+
try {
|
|
69
|
+
store.ledgerCheckpoint("system", now, { head });
|
|
70
|
+
}
|
|
71
|
+
catch { /* a busy database: the next delivery tries again */ }
|
|
72
|
+
}
|
|
53
73
|
/** A destination's name in its status row: a digest of its address (the address itself may carry a key). */
|
|
54
74
|
export const targetOf = (address) => hex(`standing-orders/monitoring/${address}`, 16);
|
|
55
75
|
/** Send what's due to one destination: hold it, start it over if it now points elsewhere, read after its cursor,
|
|
@@ -57,26 +77,30 @@ export const targetOf = (address) => hex(`standing-orders/monitoring/${address}`
|
|
|
57
77
|
async function deliver(store, sink, target, start, deps, send) {
|
|
58
78
|
const now = deps.now ?? (() => new Date());
|
|
59
79
|
const at = now();
|
|
80
|
+
const none = { count: 0, head: null };
|
|
60
81
|
if (!store.holdMonitoring(sink, deps.holder, at, new Date(at.getTime() + LEASE_MS)))
|
|
61
|
-
return
|
|
82
|
+
return none;
|
|
62
83
|
let status = store.monitoringStatus(sink)[0];
|
|
63
84
|
if (status === undefined || status.target !== target) {
|
|
64
85
|
store.resetMonitoring(sink, target, start());
|
|
65
86
|
status = store.monitoringStatus(sink)[0];
|
|
66
87
|
}
|
|
67
88
|
if (status.nextTryAt != null && Date.parse(status.nextTryAt) > at.getTime())
|
|
68
|
-
return
|
|
89
|
+
return none;
|
|
69
90
|
const from = status.through;
|
|
70
91
|
const entries = store.sealedAfter(from, BATCH);
|
|
71
92
|
if (entries.length === 0)
|
|
72
|
-
return
|
|
93
|
+
return none;
|
|
94
|
+
const last = entries[entries.length - 1];
|
|
73
95
|
try {
|
|
74
96
|
const count = await send(entries);
|
|
75
|
-
|
|
97
|
+
if (!store.monitoringDelivered(sink, deps.holder, target, from, last.id, count, now()))
|
|
98
|
+
return none;
|
|
99
|
+
return { count, head: last.seal === null ? null : { through: last.id, hash: last.seal.hash } };
|
|
76
100
|
}
|
|
77
101
|
catch (error) {
|
|
78
102
|
store.monitoringFailed(sink, deps.holder, target, error instanceof Error ? error.message : String(error), now(), new Date(now().getTime() + backoff(status.failures + 1)));
|
|
79
|
-
return
|
|
103
|
+
return none;
|
|
80
104
|
}
|
|
81
105
|
}
|
|
82
106
|
async function post(url, body, headers, deps) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Notification, Store } from "./store.js";
|
|
2
|
+
type Claim = Notification & {
|
|
3
|
+
destination: string;
|
|
4
|
+
generation: number;
|
|
5
|
+
};
|
|
6
|
+
/** URLs and shell commands can contain secrets. Only a destination hash enters saved receipts. */
|
|
7
|
+
export declare function notificationDestination(kind: string, target: string): string;
|
|
8
|
+
/** One-release compatibility for operator-configured, installation-wide senders. Personal notifications never leave here. */
|
|
9
|
+
export declare function claimNotifications(store: Store, destination: string, owner: string, now: Date): Claim[];
|
|
10
|
+
export declare function finalizeNotification(store: Store, row: Claim, owner: string, outcome: {
|
|
11
|
+
ok: true;
|
|
12
|
+
receipt: string | null;
|
|
13
|
+
} | {
|
|
14
|
+
ok: false;
|
|
15
|
+
error: string;
|
|
16
|
+
}, now: Date): boolean;
|
|
17
|
+
/** Recheck after earlier network sends: expiry or resolution must stop this send, not just its receipt. */
|
|
18
|
+
export declare function notificationClaimHeld(store: Store, row: Claim, owner: string, now: Date): boolean;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
/** URLs and shell commands can contain secrets. Only a destination hash enters saved receipts. */
|
|
3
|
+
export function notificationDestination(kind, target) {
|
|
4
|
+
return `${kind}:${createHash("sha256").update(target).digest("hex")}`;
|
|
5
|
+
}
|
|
6
|
+
/** One-release compatibility for operator-configured, installation-wide senders. Personal notifications never leave here. */
|
|
7
|
+
export function claimNotifications(store, destination, owner, now) {
|
|
8
|
+
return store.transact(() => {
|
|
9
|
+
const db = store.handle, stamp = now.toISOString();
|
|
10
|
+
db.prepare(`INSERT OR IGNORE INTO notification_delivery(notification, destination)
|
|
11
|
+
SELECT id, ? FROM notification WHERE resolved_at IS NULL AND recipient IS NULL`).run(destination);
|
|
12
|
+
const claims = db.prepare(`SELECT d.notification, d.claim_generation FROM notification_delivery d
|
|
13
|
+
JOIN notification n ON n.id = d.notification WHERE d.destination = ? AND n.resolved_at IS NULL AND n.recipient IS NULL
|
|
14
|
+
AND d.delivered_at IS NULL AND (d.receipt IS NULL OR d.receipt NOT LIKE 'skipped:%')
|
|
15
|
+
AND (d.claim_owner IS NULL OR d.claim_expires_at <= ?) ORDER BY n.id`).all(destination, stamp);
|
|
16
|
+
return claims.map(raw => {
|
|
17
|
+
const id = Number(raw["notification"]), generation = Number(raw["claim_generation"]) + 1;
|
|
18
|
+
db.prepare(`UPDATE notification_delivery SET claim_owner = ?, claim_expires_at = ?, claim_generation = ?
|
|
19
|
+
WHERE notification = ? AND destination = ?`).run(owner, new Date(now.getTime() + 60_000).toISOString(), generation, id, destination);
|
|
20
|
+
return { ...store.notificationById(id), destination, generation };
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export function finalizeNotification(store, row, owner, outcome, now) {
|
|
25
|
+
const skipped = outcome.ok && outcome.receipt?.startsWith("skipped:");
|
|
26
|
+
return Number(store.handle.prepare(`UPDATE notification_delivery SET attempts = attempts + 1, last_attempt_at = ?,
|
|
27
|
+
delivered_at = ?, receipt = ?, last_error = ?, claim_owner = NULL, claim_expires_at = NULL
|
|
28
|
+
WHERE notification = ? AND destination = ? AND claim_owner = ? AND claim_generation = ?
|
|
29
|
+
AND claim_expires_at > ? AND delivered_at IS NULL`).run(now.toISOString(), outcome.ok && !skipped ? now.toISOString() : null, outcome.ok ? outcome.receipt : null, outcome.ok ? null : outcome.error, row.id, row.destination, owner, row.generation, now.toISOString()).changes) === 1;
|
|
30
|
+
}
|
|
31
|
+
/** Recheck after earlier network sends: expiry or resolution must stop this send, not just its receipt. */
|
|
32
|
+
export function notificationClaimHeld(store, row, owner, now) {
|
|
33
|
+
return store.handle.prepare(`SELECT 1 FROM notification_delivery d JOIN notification n ON n.id = d.notification
|
|
34
|
+
WHERE d.notification = ? AND d.destination = ? AND d.claim_owner = ? AND d.claim_generation = ?
|
|
35
|
+
AND d.claim_expires_at > ? AND d.delivered_at IS NULL AND n.resolved_at IS NULL AND n.recipient IS NULL`)
|
|
36
|
+
.get(row.id, row.destination, owner, row.generation, now.toISOString()) !== undefined;
|
|
37
|
+
}
|
|
@@ -10,7 +10,7 @@ const args = (positionals = '', values = '', references = {}) => ({
|
|
|
10
10
|
* digest. Neither dereferences a conversation, task or proposal. Creation IDs are never looked up. */
|
|
11
11
|
export const REMOTE_ARGUMENTS = new Map([
|
|
12
12
|
...['status', 'ready', 'grants', 'gaps', 'sync', 'runner list', 'coordinator list', 'incident list',
|
|
13
|
-
'
|
|
13
|
+
'intake show', 'intake run', 'template list', 'webhook status', 'review show',
|
|
14
14
|
'chat-approval show', 'chat-approval off'].map(row => [row, args()]),
|
|
15
15
|
['integrations', args('', 'saved')],
|
|
16
16
|
// --person and --token-name are names compared inside the reader's own view; --token stays a refused credential.
|
|
@@ -31,7 +31,7 @@ export const REMOTE_ARGUMENTS = new Map([
|
|
|
31
31
|
['task next', args('id:task', 'key undo')],
|
|
32
32
|
['task steer', args('id:task', 'note')],
|
|
33
33
|
['task assign', args('id:task', 'key runner anyone')],
|
|
34
|
-
['task scope', args('id:task', 'key goal not touches budget-usd risk acceptance candidate
|
|
34
|
+
['task scope', args('id:task', 'key goal not touches budget-usd risk acceptance candidate model provider repair-model')],
|
|
35
35
|
['task plan', args('id:task', 'key provider model')],
|
|
36
36
|
['task hold', args('id:task', 'key reason until')],
|
|
37
37
|
['task unhold', args('id:task', 'key')],
|
|
@@ -47,13 +47,9 @@ export const REMOTE_ARGUMENTS = new Map([
|
|
|
47
47
|
['cap list', args()], ['cap add', args('name', 'kind probe expires')],
|
|
48
48
|
// These objects are installation-scoped at the outer boundary; only an all-project principal reaches them.
|
|
49
49
|
['incident resolve', args('incident')],
|
|
50
|
-
['routine show', args('id')],
|
|
51
|
-
['routine add', args('name', 'goal schedule acceptance ceiling budget-usd not touches require')],
|
|
52
|
-
...['refresh', 'pause', 'resume', 'run-now'].map(action => [`routine ${action}`, args('name')]),
|
|
53
50
|
['config show', args('phase?')], ['verify show', args()],
|
|
54
51
|
['intake preview', args()], ['intake pr-comments', args('', 'limit')],
|
|
55
52
|
['template show', args('name')],
|
|
56
|
-
['contest show', args('id')], ['contest exclude', args('contest ordinal')],
|
|
57
53
|
...['search', 'impact'].map(action => [`knowledge ${action}`, args('query', 'base')]),
|
|
58
54
|
['knowledge refresh', args('', 'base')],
|
|
59
55
|
// Decision reads and supersession are already project-bound in project-memory.ts. Proposal IDs are
|
package/dist/operate-remote.js
CHANGED
|
@@ -43,10 +43,9 @@ export const REMOTE_SCOPES = new Map([
|
|
|
43
43
|
["task list", listing],
|
|
44
44
|
["runner list", installation], ["coordinator list", installation], ["outbox list", installation], ["incident list", installation], ["incident resolve", installation],
|
|
45
45
|
["cap list", repo], ["cap add", repo],
|
|
46
|
-
...["list", "show", "add", "refresh", "pause", "resume", "run-now"].map(action => [`routine ${action}`, installation]),
|
|
47
46
|
["config show", installation], ["verify show", repo], ["intake show", installation], ["intake run", installation], ["intake preview", installation],
|
|
48
|
-
["intake pr-comments", installation], ["template list", installation], ["template show", installation],
|
|
49
|
-
["
|
|
47
|
+
["intake pr-comments", installation], ["template list", installation], ["template show", installation],
|
|
48
|
+
["webhook status", installation], ["review show", installation],
|
|
50
49
|
["chat-approval show", self], ["chat-approval off", self],
|
|
51
50
|
// The person's own remote actions, or everyone's within their projects for an approver (remote-audit.ts).
|
|
52
51
|
["audit", self],
|
|
@@ -63,11 +62,11 @@ export const REMOTE_SCOPES = new Map([
|
|
|
63
62
|
*/
|
|
64
63
|
export const REMOTE_ALL_PROJECTS_ONLY = new Map([
|
|
65
64
|
// Settings, connections and acts that belong to the installation as a whole, not to any one project.
|
|
66
|
-
...["integrations", "grants", "sync", "incident resolve",
|
|
67
|
-
"config show", "intake run", "
|
|
65
|
+
...["integrations", "grants", "sync", "incident resolve",
|
|
66
|
+
"config show", "intake run", "webhook status"].map(row => [row, "installation-wide"]),
|
|
68
67
|
// Cross-project lists and reads a limited person is still refused; scoping them is later work.
|
|
69
|
-
...["runner list", "coordinator list", "outbox list", "incident list", "
|
|
70
|
-
"intake pr-comments", "template list", "template show", "
|
|
68
|
+
...["runner list", "coordinator list", "outbox list", "incident list", "intake show", "intake preview",
|
|
69
|
+
"intake pr-comments", "template list", "template show", "review show"].map(row => [row, "not yet scoped"]),
|
|
71
70
|
]);
|
|
72
71
|
/** The projects a listing or task reference may disclose: null for someone with access to all of them. */
|
|
73
72
|
export function remoteLensOf(store, scope, allows) {
|
package/dist/operate.d.ts
CHANGED
|
@@ -73,10 +73,6 @@ export type OperateOptions = {
|
|
|
73
73
|
integrationIo?: Partial<IntegrationIo>;
|
|
74
74
|
/** Injected by tests: the mate's provider fetch, key environment, and stdin lines. */
|
|
75
75
|
mateSeams?: MateCliSeams;
|
|
76
|
-
/** Injected by tests: a held-session coordinator, so a `tick` exercises
|
|
77
|
-
* the attended road exactly as a co-located `up` would (production wires
|
|
78
|
-
* one only inside `up`). */
|
|
79
|
-
heldCoordinator?: import("./held.js").HeldSessionCoordinator;
|
|
80
76
|
/** Test seam for the short indexed wait loop. */
|
|
81
77
|
waitSleep?: (milliseconds: number) => Promise<void>;
|
|
82
78
|
/** Injected by tests: `onboard`'s home folder, terminal and probes. */
|
|
@@ -110,8 +106,6 @@ export declare const TASK_ACTIONS: readonly ["add", "list", "show", "wait", "sta
|
|
|
110
106
|
export declare const PUBLISH_ACTIONS: readonly ["setup", "grant", "revoke", "status", "unblock", "rearm", "merge", "refire"];
|
|
111
107
|
export declare const CONFIG_ACTIONS: readonly ["show", "set", "clear"];
|
|
112
108
|
export declare const APPROVER_ACTIONS: readonly ["list", "add"];
|
|
113
|
-
export declare const ROUTINE_ACTIONS: readonly ["list", "add", "show", "approve", "refresh", "pause", "resume", "run-now"];
|
|
114
|
-
export declare const CONTEST_ACTIONS: readonly ["show", "exclude"];
|
|
115
109
|
export declare const PEOPLE_ACTIONS: readonly ["list", "invite", "projects", "revoke"];
|
|
116
110
|
export declare const KEYS_ACTIONS: readonly ["status", "set", "clear", "verify", "auth"];
|
|
117
111
|
/**
|