toolroll 0.9.52 → 0.9.54
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 +16 -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/board.d.ts +3 -19
- package/dist/board.js +4 -42
- package/dist/browser/workspace.js +18 -18
- package/dist/browser-workspace.d.ts +2 -1
- package/dist/builder.d.ts +9 -40
- package/dist/builder.js +50 -257
- package/dist/chat-actions.js +3 -14
- package/dist/chat-ask.js +8 -8
- package/dist/chat-controls.d.ts +0 -4
- package/dist/chat-controls.js +0 -1
- package/dist/chat-core.d.ts +171 -0
- package/dist/chat-core.js +288 -0
- package/dist/chat-decide.d.ts +10 -8
- package/dist/chat-decide.js +27 -24
- package/dist/chat-delivery-state.d.ts +51 -75
- package/dist/chat-delivery-state.js +140 -170
- package/dist/chat-delivery.d.ts +21 -4
- package/dist/chat-delivery.js +313 -195
- package/dist/chat-flow.d.ts +29 -1
- package/dist/chat-flow.js +24 -50
- package/dist/chat-messages.d.ts +319 -0
- package/dist/chat-messages.js +841 -0
- package/dist/chat-migration.d.ts +30 -0
- package/dist/chat-migration.js +802 -0
- package/dist/chat-quiet.d.ts +3 -0
- package/dist/chat-quiet.js +7 -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/chat-content.d.ts +71 -2
- package/dist/contracts/chat-content.js +22 -3
- package/dist/contracts/chat-tables.d.ts +11 -4
- package/dist/contracts/chat-tables.js +165 -87
- 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/discord-callback.js +2 -1
- 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/slack-callback.js +3 -2
- package/dist/contracts/store-json.d.ts +1 -29
- package/dist/contracts/store-json.js +4 -17
- package/dist/contracts/teams-callback.js +2 -1
- 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/demo.js +16 -26
- package/dist/desktop-update-gate.js +0 -1
- package/dist/desktop-update.js +1 -3
- package/dist/discord-chat.js +19 -38
- package/dist/discord-settings.js +4 -13
- package/dist/discord.js +4 -14
- 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 +8 -8
- 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/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 +13 -24
- package/dist/modes.d.ts +19 -8
- package/dist/modes.js +24 -14
- package/dist/notification-rows.d.ts +30 -0
- package/dist/notification-rows.js +102 -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 +226 -2248
- package/dist/phase-routing.d.ts +13 -23
- package/dist/phase-routing.js +13 -35
- package/dist/plan-auto.js +2 -2
- package/dist/plane-review.js +5 -11
- 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/project-delete.js +24 -41
- 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/result-shots.js +2 -2
- package/dist/retention.js +1 -1
- 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 +15 -24
- 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 +6 -12
- package/dist/server/shared.d.ts +28 -212
- package/dist/server/shared.js +71 -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-api.d.ts +5 -1
- package/dist/slack-api.js +2 -2
- package/dist/slack-chat.d.ts +9 -6
- package/dist/slack-chat.js +29 -53
- package/dist/slack-settings.js +6 -15
- package/dist/slack.js +6 -16
- package/dist/store.d.ts +200 -1702
- package/dist/store.js +811 -5439
- package/dist/surface.js +10 -24
- 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/teammate-question.js +12 -12
- package/dist/teammates-ui.js +1 -1
- package/dist/teams-chat.js +12 -9
- package/dist/teams-settings.js +2 -2
- package/dist/teams.js +4 -5
- package/dist/telegram-flow.js +5 -5
- package/dist/telegram-mate.d.ts +0 -3
- package/dist/telegram-mate.js +0 -1
- package/dist/telegram.js +4 -4
- package/dist/templates.d.ts +11 -7
- package/dist/templates.js +12 -9
- package/dist/toolroll-update.d.ts +20 -0
- package/dist/toolroll-update.js +104 -5
- package/dist/work-index.js +2 -6
- package/dist/workspace-revision.js +3 -1
- package/dist/workspace-ui.js +1 -1
- package/package.json +1 -1
- 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/slack-state.d.ts +0 -13
- package/dist/slack-state.js +0 -10
- package/dist/supervisor.mjs +0 -297
package/dist/chat-decide.d.ts
CHANGED
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
* their own chat; otherwise, and for a plan that widens permissions, exceeds the limit or touches protected paths, the
|
|
10
10
|
* card keeps its link.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
* tokens (the `channel` column). Every act runs through the door the
|
|
12
|
+
* The same in every chat app: Telegram's bridge and the shared engine Slack, Discord and Teams run on (chat-delivery.ts)
|
|
13
|
+
* offer, mint and answer these through the same tokens (the `channel` column). Every act runs through the door the
|
|
14
|
+
* console uses. */
|
|
14
15
|
import { DECIDE_ACTS } from "./contracts/chat-callback-rows.js";
|
|
15
16
|
import { type ChatApprovalLimits } from "./chat-approval.js";
|
|
16
17
|
import { type VerifiedApprover } from "./principal.js";
|
|
@@ -78,10 +79,11 @@ export type DecideOffer = {
|
|
|
78
79
|
text: string | null;
|
|
79
80
|
rows: Array<Array<ActSpec | DecideLink>>;
|
|
80
81
|
};
|
|
81
|
-
/** What lets this person's chat approve on this repository now: their lasting chat-approval setting
|
|
82
|
-
* or else a live signed mode carrying the chatApprove term, signed by them
|
|
82
|
+
/** What lets this person's chat on `channel` approve on this repository now: their lasting chat-approval setting
|
|
83
|
+
* (chat-approval.ts), or else a live signed mode carrying the chatApprove term, signed by them, that names this chat
|
|
84
|
+
* app (a mode signed before the term named its apps covers Telegram alone). `digest` names that authority for the
|
|
83
85
|
* ledger; the yes itself is the person's tap and is sealed as theirs, so it doesn't lapse when the authority does. */
|
|
84
|
-
export declare function chatApproveMode(store: Store, repo: string | null, approver: string, now: Date): {
|
|
86
|
+
export declare function chatApproveMode(store: Store, repo: string | null, approver: string, now: Date, channel: DecideChannel): {
|
|
85
87
|
ok: true;
|
|
86
88
|
digest: string;
|
|
87
89
|
limits: ChatApprovalLimits;
|
|
@@ -94,7 +96,7 @@ export declare function chatApproveMode(store: Store, repo: string | null, appro
|
|
|
94
96
|
export declare function permissionPlainWords(profile: Scope["profile"] | null | undefined): string | null;
|
|
95
97
|
/** A plan this person's chat may approve now: the term, the plan's exact bytes, and none of the reasons it must open
|
|
96
98
|
* Toolroll instead. `digest` binds the scope and the plan document the card shows. */
|
|
97
|
-
export declare function planInChat(store: Store, taskId: string, approver: string, now: Date, root?: string): {
|
|
99
|
+
export declare function planInChat(store: Store, taskId: string, approver: string, now: Date, channel: DecideChannel, root?: string): {
|
|
98
100
|
ok: true;
|
|
99
101
|
modeDigest: string;
|
|
100
102
|
source: "setting" | "mode";
|
|
@@ -105,7 +107,7 @@ export declare function planInChat(store: Store, taskId: string, approver: strin
|
|
|
105
107
|
why: string;
|
|
106
108
|
};
|
|
107
109
|
/** A ready pull request this person's chat may merge now, bound to the exact commit. */
|
|
108
|
-
export declare function mergeInChat(store: Store, taskId: string, run: number, approver: string, now: Date): {
|
|
110
|
+
export declare function mergeInChat(store: Store, taskId: string, run: number, approver: string, now: Date, channel: DecideChannel): {
|
|
109
111
|
ok: true;
|
|
110
112
|
modeDigest: string;
|
|
111
113
|
source: "setting" | "mode";
|
|
@@ -119,7 +121,7 @@ export declare function mergeInChat(store: Store, taskId: string, run: number, a
|
|
|
119
121
|
export declare function planCardText(store: Store, taskId: string, scope: Scope, root?: string, channel?: DecideChannel): string;
|
|
120
122
|
/** The acts a card about this target can take in chat right now, or null when none can (the card keeps its own
|
|
121
123
|
* link). Reads only; nothing is minted. */
|
|
122
|
-
export declare function decideOffer(store: Store, target: DecideTarget, who: VerifiedApprover, now: Date,
|
|
124
|
+
export declare function decideOffer(store: Store, target: DecideTarget, who: VerifiedApprover, now: Date, channel: DecideChannel, root?: string): DecideOffer | null;
|
|
123
125
|
/** One fingerprint of what the card offers, without its tokens: a repaint with the same offer keeps its buttons. */
|
|
124
126
|
export declare function offerFingerprint(offer: DecideOffer): string;
|
|
125
127
|
/** The card's buttons, minted now (message unknown until it is sent: `placeDecideTokens` puts them on it). */
|
package/dist/chat-decide.js
CHANGED
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
* their own chat; otherwise, and for a plan that widens permissions, exceeds the limit or touches protected paths, the
|
|
10
10
|
* card keeps its link.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
* tokens (the `channel` column). Every act runs through the door the
|
|
12
|
+
* The same in every chat app: Telegram's bridge and the shared engine Slack, Discord and Teams run on (chat-delivery.ts)
|
|
13
|
+
* offer, mint and answer these through the same tokens (the `channel` column). Every act runs through the door the
|
|
14
|
+
* console uses. */
|
|
14
15
|
import { DECIDE_ACTS, readDecideActionRow } from "./contracts/chat-callback-rows.js";
|
|
15
16
|
import { createHash, randomBytes } from "node:crypto";
|
|
16
17
|
import { acceptAndCompleteAsOperator, assignmentOf, owedAcceptance } from "./assignment.js";
|
|
@@ -19,7 +20,7 @@ import { applyChatTaskAction, chatTaskStamp } from "./chat-task-actions.js";
|
|
|
19
20
|
import { chatTitle } from "./chat-voice.js";
|
|
20
21
|
import { readVerifiedArtifact } from "./evidence.js";
|
|
21
22
|
import { effectiveChatApproval } from "./chat-approval.js";
|
|
22
|
-
import { modeTermsFromJson } from "./modes.js";
|
|
23
|
+
import { chatApproveChatsOf, chatNames, modeTermsFromJson } from "./modes.js";
|
|
23
24
|
import { parseExecutionPlanDocument } from "./plan.js";
|
|
24
25
|
import { verifyApproverStanding } from "./principal.js";
|
|
25
26
|
import { pullRequestFollowOf } from "./pull-request-flow.js";
|
|
@@ -47,10 +48,11 @@ const sha = (text) => createHash("sha256").update(text).digest("hex");
|
|
|
47
48
|
const isAct = (one) => "act" in one;
|
|
48
49
|
const title = (store, taskId) => phoneText(chatTitle(store, taskId), 60);
|
|
49
50
|
// ---- the chatApprove term ----------------------------------------------------------
|
|
50
|
-
/** What lets this person's chat approve on this repository now: their lasting chat-approval setting
|
|
51
|
-
* or else a live signed mode carrying the chatApprove term, signed by them
|
|
51
|
+
/** What lets this person's chat on `channel` approve on this repository now: their lasting chat-approval setting
|
|
52
|
+
* (chat-approval.ts), or else a live signed mode carrying the chatApprove term, signed by them, that names this chat
|
|
53
|
+
* app (a mode signed before the term named its apps covers Telegram alone). `digest` names that authority for the
|
|
52
54
|
* ledger; the yes itself is the person's tap and is sealed as theirs, so it doesn't lapse when the authority does. */
|
|
53
|
-
export function chatApproveMode(store, repo, approver, now) {
|
|
55
|
+
export function chatApproveMode(store, repo, approver, now, channel) {
|
|
54
56
|
if (repo === null)
|
|
55
57
|
return { ok: false, why: "Approving from chat isn't turned on for this project." };
|
|
56
58
|
const setting = effectiveChatApproval(store, repo, approver);
|
|
@@ -62,6 +64,9 @@ export function chatApproveMode(store, repo, approver, now) {
|
|
|
62
64
|
return setting;
|
|
63
65
|
if (mode.signedBy !== approver || !store.accountCanAccess(approver, repo))
|
|
64
66
|
return { ok: false, why: "Only the person who signed this project's mode can approve from chat." };
|
|
67
|
+
const chats = chatApproveChatsOf(terms);
|
|
68
|
+
if (!chats.includes(channel))
|
|
69
|
+
return { ok: false, why: `Your mode approves from ${chatNames(chats)} only. Sign it again to approve from ${chatNames([channel])}.` };
|
|
65
70
|
return { ok: true, digest: mode.digest, limits: { fullAccess: terms.permissionDefault === "escalated", capMicrousd: terms.perAttemptBudgetMicrousd }, source: "mode" };
|
|
66
71
|
}
|
|
67
72
|
/** What a plan's yes allows an agent to do, in a few plain words (the approval sheet's "You're allowing"). */
|
|
@@ -87,7 +92,7 @@ function fullAccess(profile) {
|
|
|
87
92
|
const money = (micro) => `$${(micro / 1_000_000).toFixed(2)}`;
|
|
88
93
|
/** A plan this person's chat may approve now: the term, the plan's exact bytes, and none of the reasons it must open
|
|
89
94
|
* Toolroll instead. `digest` binds the scope and the plan document the card shows. */
|
|
90
|
-
export function planInChat(store, taskId, approver, now, root) {
|
|
95
|
+
export function planInChat(store, taskId, approver, now, channel, root) {
|
|
91
96
|
const ref = store.lookupRef(taskId);
|
|
92
97
|
const scope = store.getScope(taskId);
|
|
93
98
|
if (ref === null || ref.repo === null || scope === null)
|
|
@@ -96,7 +101,7 @@ export function planInChat(store, taskId, approver, now, root) {
|
|
|
96
101
|
return { ok: false, why: "This plan is already approved." };
|
|
97
102
|
if (ref.plan === "requested")
|
|
98
103
|
return { ok: false, why: "The plan is still being written." };
|
|
99
|
-
const mode = chatApproveMode(store, ref.repo, approver, now);
|
|
104
|
+
const mode = chatApproveMode(store, ref.repo, approver, now, channel);
|
|
100
105
|
if (!mode.ok)
|
|
101
106
|
return mode;
|
|
102
107
|
if (store.getTask(taskId)?.state === "cancelled" || store.getTask(taskId)?.state === "done")
|
|
@@ -104,11 +109,9 @@ export function planInChat(store, taskId, approver, now, root) {
|
|
|
104
109
|
const family = store.taskFamilyOf(taskId, [ref.repo], false);
|
|
105
110
|
if (family === null || family.problem !== null || family.current.id !== taskId)
|
|
106
111
|
return { ok: false, why: "A newer version of this task is current." };
|
|
107
|
-
// What the mode's seal refuses anyway, said first: model- or coordinator-written terms,
|
|
112
|
+
// What the mode's seal refuses anyway, said first: model- or coordinator-written terms, an unreadable route.
|
|
108
113
|
if (ref.coordinatorCid !== null || (scope.proposedVia ?? null) !== null)
|
|
109
114
|
return { ok: false, why: "This plan was written for you, so you approve it in Toolroll." };
|
|
110
|
-
if (store.activeTournamentTerms(ref.id) !== null)
|
|
111
|
-
return { ok: false, why: "Agents compete on this task, so you approve it in Toolroll." };
|
|
112
115
|
if (scope.profileState === "unresolved" || scope.routeEra == null || (scope.termsProblem ?? null) !== null)
|
|
113
116
|
return { ok: false, why: "This plan can't say exactly which agents run, so you approve it in Toolroll." };
|
|
114
117
|
const yours = mode.source === "setting" ? "your chat approval setting" : "your mode";
|
|
@@ -150,7 +153,7 @@ export function planInChat(store, taskId, approver, now, root) {
|
|
|
150
153
|
return { ok: true, modeDigest: mode.digest, source: mode.source, scope, digest: sha(`${scope.digest}:${plan?.sha256 ?? ""}`) };
|
|
151
154
|
}
|
|
152
155
|
/** A ready pull request this person's chat may merge now, bound to the exact commit. */
|
|
153
|
-
export function mergeInChat(store, taskId, run, approver, now) {
|
|
156
|
+
export function mergeInChat(store, taskId, run, approver, now, channel) {
|
|
154
157
|
const ref = store.lookupRef(taskId);
|
|
155
158
|
const publication = store.publicationForRun(run);
|
|
156
159
|
const follow = publication === null ? null : pullRequestFollowOf(store, publication.id);
|
|
@@ -160,7 +163,7 @@ export function mergeInChat(store, taskId, run, approver, now) {
|
|
|
160
163
|
return { ok: false, why: "This pull request is already merged." };
|
|
161
164
|
if (follow.readyHead !== publication.headSha)
|
|
162
165
|
return { ok: false, why: "This pull request changed since this card was sent." };
|
|
163
|
-
const mode = chatApproveMode(store, ref.repo, approver, now);
|
|
166
|
+
const mode = chatApproveMode(store, ref.repo, approver, now, channel);
|
|
164
167
|
if (!mode.ok)
|
|
165
168
|
return mode;
|
|
166
169
|
return { ok: true, modeDigest: mode.digest, source: mode.source, head: publication.headSha };
|
|
@@ -218,7 +221,7 @@ export function planCardText(store, taskId, scope, root, channel) {
|
|
|
218
221
|
// ---- what a card offers ---------------------------------------------------------------
|
|
219
222
|
/** The acts a card about this target can take in chat right now, or null when none can (the card keeps its own
|
|
220
223
|
* link). Reads only; nothing is minted. */
|
|
221
|
-
export function decideOffer(store, target, who, now,
|
|
224
|
+
export function decideOffer(store, target, who, now, channel, root) {
|
|
222
225
|
const ref = store.lookupRef(target.taskId);
|
|
223
226
|
if (ref === null || ref.repo === null || !who.repos.includes(ref.repo))
|
|
224
227
|
return null;
|
|
@@ -243,14 +246,14 @@ export function decideOffer(store, target, who, now, root, channel) {
|
|
|
243
246
|
return { text: null, rows: [[{ act: "retry", run: null, digest: stamp }], [{ label: DECIDE_LABELS.look, path: chatControlHref("task", target.taskId) }]] };
|
|
244
247
|
}
|
|
245
248
|
case "plan": {
|
|
246
|
-
const plan = planInChat(store, target.taskId, who.name, now, root);
|
|
249
|
+
const plan = planInChat(store, target.taskId, who.name, now, channel, root);
|
|
247
250
|
if (!plan.ok)
|
|
248
251
|
return null;
|
|
249
252
|
return { text: planCardText(store, target.taskId, plan.scope, root, channel),
|
|
250
253
|
rows: [[{ act: "approve", run: null, digest: plan.digest }], [{ label: DECIDE_LABELS.edit, path: chatControlHref("approval", target.taskId) }, { act: "not-now", run: null, digest: plan.digest }]] };
|
|
251
254
|
}
|
|
252
255
|
case "merge": {
|
|
253
|
-
const merge = mergeInChat(store, target.taskId, target.run, who.name, now);
|
|
256
|
+
const merge = mergeInChat(store, target.taskId, target.run, who.name, now, channel);
|
|
254
257
|
if (!merge.ok)
|
|
255
258
|
return null;
|
|
256
259
|
return { text: null, rows: [[{ act: "merge", run: target.run, digest: merge.head }], [{ label: DECIDE_LABELS.look, path: chatResultHref(target.taskId, target.run, "changes") }]] };
|
|
@@ -350,7 +353,7 @@ function targetOf(row) {
|
|
|
350
353
|
}
|
|
351
354
|
}
|
|
352
355
|
/** Why the card's act no longer applies, or null while it still does — checked at the offer and again at the yes. */
|
|
353
|
-
function staleWhy(store, row, who, now, root) {
|
|
356
|
+
function staleWhy(store, row, who, now, channel, root) {
|
|
354
357
|
switch (row.act) {
|
|
355
358
|
case "accept":
|
|
356
359
|
case "changes": {
|
|
@@ -368,13 +371,13 @@ function staleWhy(store, row, who, now, root) {
|
|
|
368
371
|
}
|
|
369
372
|
case "approve":
|
|
370
373
|
case "not-now": {
|
|
371
|
-
const plan = planInChat(store, row.taskId, who.name, now, root);
|
|
374
|
+
const plan = planInChat(store, row.taskId, who.name, now, channel, root);
|
|
372
375
|
if (!plan.ok)
|
|
373
376
|
return plan.why;
|
|
374
377
|
return plan.digest === row.digest ? null : "The plan changed since this card was sent.";
|
|
375
378
|
}
|
|
376
379
|
case "merge": {
|
|
377
|
-
const merge = mergeInChat(store, row.taskId, row.run ?? 0, who.name, now);
|
|
380
|
+
const merge = mergeInChat(store, row.taskId, row.run ?? 0, who.name, now, channel);
|
|
378
381
|
if (!merge.ok)
|
|
379
382
|
return merge.why;
|
|
380
383
|
return merge.head === row.digest ? null : "This pull request changed since this card was sent.";
|
|
@@ -461,13 +464,13 @@ export function applyDecideTap(store, seat, input) {
|
|
|
461
464
|
}
|
|
462
465
|
if (row.phase === "cancel") {
|
|
463
466
|
spend();
|
|
464
|
-
const offer = decideOffer(store, target, who, now,
|
|
467
|
+
const offer = decideOffer(store, target, who, now, seat.channel, input.root);
|
|
465
468
|
if (offer === null)
|
|
466
469
|
return { ack: "Cancelled.", edit: { text: body, rows: linksFor(target), tokens: [] } };
|
|
467
470
|
const minted = mintDecideButtons(store, seat, target, offer, now, input.message);
|
|
468
471
|
return { ack: "Cancelled.", edit: { text: offer.text ?? body, ...minted } };
|
|
469
472
|
}
|
|
470
|
-
const why = staleWhy(store, row, who, now, input.root);
|
|
473
|
+
const why = staleWhy(store, row, who, now, seat.channel, input.root);
|
|
471
474
|
if (why !== null)
|
|
472
475
|
return stale(why);
|
|
473
476
|
if (row.phase === "offer") {
|
|
@@ -505,7 +508,7 @@ export function applyDecideTap(store, seat, input) {
|
|
|
505
508
|
return retried.ok ? done("✓ Queued again. Existing approvals and holds still apply.") : notDone(retried.message);
|
|
506
509
|
}
|
|
507
510
|
case "approve": {
|
|
508
|
-
const plan = planInChat(store, row.taskId, who.name, now, input.root);
|
|
511
|
+
const plan = planInChat(store, row.taskId, who.name, now, seat.channel, input.root);
|
|
509
512
|
if (!plan.ok)
|
|
510
513
|
return notDone(plan.why);
|
|
511
514
|
// The owner's own yes, sealed as theirs ("chat"), never as a mode's: it stands like the console's after the mode or setting ends.
|
|
@@ -516,7 +519,7 @@ export function applyDecideTap(store, seat, input) {
|
|
|
516
519
|
return done("✓ Approved. Work starts when a worker is free.");
|
|
517
520
|
}
|
|
518
521
|
case "merge": {
|
|
519
|
-
const merge = mergeInChat(store, row.taskId, row.run ?? 0, who.name, now);
|
|
522
|
+
const merge = mergeInChat(store, row.taskId, row.run ?? 0, who.name, now, seat.channel);
|
|
520
523
|
if (!merge.ok)
|
|
521
524
|
return notDone(merge.why);
|
|
522
525
|
const via = `via ${seat.channel} · chat binding #${seat.binding} · ${merge.source} ${merge.modeDigest} · ${merge.head.slice(0, 12)}`;
|
|
@@ -569,7 +572,7 @@ export function applyDecideFeedback(store, seat, prompt, text, repos, root, now)
|
|
|
569
572
|
const verified = verifyApproverStanding(store, seat.approver, seat.generation, repos);
|
|
570
573
|
if (!verified.ok)
|
|
571
574
|
return { said: "That result isn't available here now. Nothing was sent.", link: null };
|
|
572
|
-
const why = staleWhy(store, { act: "changes", taskId: prompt.taskId, run: prompt.run, digest: prompt.digest }, verified.who, now, root);
|
|
575
|
+
const why = staleWhy(store, { act: "changes", taskId: prompt.taskId, run: prompt.run, digest: prompt.digest }, verified.who, now, seat.channel, root);
|
|
573
576
|
if (why !== null)
|
|
574
577
|
return { said: `${why} Your feedback wasn't sent.`, link: { label: "Open task", path: chatControlHref("task", prompt.taskId) } };
|
|
575
578
|
if (root === undefined)
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import {
|
|
1
|
+
import { ChatCore, chatHash, type ChatBinding, type ChatProvider, type ChatRoom } from "./chat-core.js";
|
|
2
|
+
import { ChatMessages } from "./chat-messages.js";
|
|
3
3
|
import { savedChatPart, type ChatContent, type ChatEventBody } from "./contracts/chat-content.js";
|
|
4
|
-
export { savedChatPart, type ChatContent, type ChatEventBody };
|
|
5
|
-
export { chatSchema, chatTables };
|
|
4
|
+
export { savedChatPart, chatHash, type ChatContent, type ChatEventBody, type ChatBinding, type ChatProvider, type ChatRoom };
|
|
6
5
|
export type ChatIdentity = {
|
|
7
6
|
installation: string;
|
|
8
7
|
team?: string;
|
|
@@ -10,18 +9,6 @@ export type ChatIdentity = {
|
|
|
10
9
|
bot: string;
|
|
11
10
|
workspace: string;
|
|
12
11
|
};
|
|
13
|
-
export type ChatBinding = {
|
|
14
|
-
id: number;
|
|
15
|
-
installation: string;
|
|
16
|
-
team: string;
|
|
17
|
-
app: string;
|
|
18
|
-
member: string;
|
|
19
|
-
channel: string;
|
|
20
|
-
approver: string;
|
|
21
|
-
generation: number;
|
|
22
|
-
created: string;
|
|
23
|
-
revoked: string | null;
|
|
24
|
-
};
|
|
25
12
|
export type ChatEvent = {
|
|
26
13
|
id: string;
|
|
27
14
|
installation: string;
|
|
@@ -51,64 +38,20 @@ export type ChatPart = {
|
|
|
51
38
|
next_at: string | null;
|
|
52
39
|
created: string;
|
|
53
40
|
};
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
kind: "group" | "private";
|
|
59
|
-
conversation: string;
|
|
60
|
-
boundBy: string;
|
|
61
|
-
binding: number;
|
|
62
|
-
cursor: number;
|
|
63
|
-
};
|
|
64
|
-
export declare const chatHash: (text: string) => string;
|
|
65
|
-
export declare class ChatState {
|
|
66
|
-
readonly store: Store;
|
|
67
|
-
readonly channel: "slack" | "discord" | "teams";
|
|
68
|
-
constructor(store: Store, channel: "slack" | "discord" | "teams");
|
|
69
|
-
prepare(sql: string): import("./store.js").Statement;
|
|
70
|
-
/** Small transport facts a channel must remember per chat (a Teams service URL). Never credentials. */
|
|
71
|
-
meta(installation: string, key: string): string | null;
|
|
72
|
-
setMeta(installation: string, key: string, value: string, now: Date): void;
|
|
73
|
-
private readRoom;
|
|
74
|
-
room(installation: string, chat: string): ChatRoom | null;
|
|
75
|
-
rooms(installation: string): ChatRoom[];
|
|
76
|
-
/** Point a chat at a conversation; an earlier choice for the same chat is revoked and delivery starts from now. */
|
|
77
|
-
bindRoom(args: {
|
|
78
|
-
installation: string;
|
|
79
|
-
chat: string;
|
|
80
|
-
kind: "group" | "private";
|
|
81
|
-
conversation: string;
|
|
82
|
-
by: string;
|
|
83
|
-
binding: number;
|
|
84
|
-
}, now: Date): {
|
|
85
|
-
ok: true;
|
|
86
|
-
} | {
|
|
87
|
-
ok: false;
|
|
88
|
-
reason: "group-taken";
|
|
89
|
-
};
|
|
90
|
-
unbindRoom(installation: string, chat: string, by: string, now: Date): boolean;
|
|
91
|
-
/** The shared rooms core's view of this installation's room records. */
|
|
92
|
-
roomBackend(installation: string, now: Date): import("./chat-rooms.js").RoomBackend;
|
|
93
|
-
advanceRoomCursor(id: number, messageId: number): boolean;
|
|
94
|
-
get db(): import("./store.js").Database;
|
|
95
|
-
/** Every unrevoked binding for an installation, oldest first: one per
|
|
96
|
-
* paired person (v73). Liveness is checked per binding with `live`. */
|
|
97
|
-
bindings(installation: string): ChatBinding[];
|
|
98
|
-
/** The unrevoked binding one channel member holds, if any. */
|
|
99
|
-
bindingFor(installation: string, member: string): ChatBinding | null;
|
|
100
|
-
bindingById(id: number): ChatBinding | null;
|
|
101
|
-
/** The oldest unrevoked binding — the single-person reading kept for
|
|
102
|
-
* status lines and fixtures; inbound, delivery and notices read
|
|
103
|
-
* `bindingFor` / `bindingById` / `bindings`. */
|
|
104
|
-
binding(installation: string): ChatBinding | null;
|
|
105
|
-
live(binding: ChatBinding): boolean;
|
|
106
|
-
/** End one person's pairing and everything their chat could still do; teammates' bindings stay. */
|
|
107
|
-
revokeBinding(binding: ChatBinding, now?: Date): void;
|
|
108
|
-
revoke(installation: string, now?: Date): void;
|
|
41
|
+
/** One app's chats on the shared core (the same object Telegram's run on): its events, planned parts and their buttons. */
|
|
42
|
+
export declare class ChatState extends ChatMessages {
|
|
43
|
+
constructor(store: ChatCore["store"], provider: ChatProvider);
|
|
44
|
+
/** Pair-time half kept for the settings pages: mint a code for this installation and return it (only its hash is kept). */
|
|
109
45
|
pairing(installation: string, approver: string, generation: number, now?: Date): string;
|
|
110
|
-
|
|
111
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Consume a code sent in a chat (the shared rules, chat-core.ts). The installation's notice cursor starts from now
|
|
48
|
+
* on its FIRST pairing; a later person joins the running cursor (their own history is fenced by their binding's
|
|
49
|
+
* creation time).
|
|
50
|
+
*/
|
|
51
|
+
pair(identity: ChatIdentity, hash: string, member: string, channel: string, now: Date, event?: string | null): ChatBinding | null;
|
|
52
|
+
/** The 60-second lease the Slack, Discord and Teams workers renew. */
|
|
53
|
+
lease(installation: string, owner: string, now: Date): boolean;
|
|
54
|
+
/** Save a received event with its body (contracts/chat-content.ts), versioned. False: it was already here. */
|
|
112
55
|
enqueue(event: Omit<ChatEvent, "session" | "state" | "next_at" | "payload"> & {
|
|
113
56
|
payload: ChatEventBody;
|
|
114
57
|
}): boolean;
|
|
@@ -116,11 +59,44 @@ export declare class ChatState {
|
|
|
116
59
|
next(installation: string, now: Date): ChatEvent | null;
|
|
117
60
|
defer(event: string, problem: string, until: Date): void;
|
|
118
61
|
finish(event: string, dropped?: boolean): void;
|
|
62
|
+
/** Point an event at the binding its pairing made. */
|
|
63
|
+
setEventBinding(event: string, binding: number): void;
|
|
119
64
|
part(id: number): ChatPart | null;
|
|
65
|
+
/** The reply to an event, part by part, with each part's buttons minted with it; the event is then handled. Planned once. */
|
|
120
66
|
plan(event: string, parts: ChatContent[], now: Date): void;
|
|
67
|
+
/** A proposal card's buttons on its part: earlier ones on that part are spent. Yes and Cancel last ten minutes. */
|
|
121
68
|
tokens(part: number, proposal: number, phases: Array<"confirm" | "dismiss" | "yes" | "cancel">, now: Date): void;
|
|
122
|
-
|
|
123
|
-
|
|
69
|
+
/** A proposal card's live buttons on one part, in the order they were minted. */
|
|
70
|
+
partTokens(part: number, now: Date): {
|
|
71
|
+
token: string;
|
|
72
|
+
phase: string;
|
|
73
|
+
}[];
|
|
74
|
+
/** The next part to send for an installation: notices only while notifications may go out. */
|
|
75
|
+
nextPart(installation: string, notices: boolean, now: Date, order?: "id" | "created"): ChatPart | null;
|
|
76
|
+
/** A part that will not go: why, in words the saved chat shows. */
|
|
77
|
+
dropPart(id: number, problem: string): void;
|
|
78
|
+
/** Try again no earlier than then (a screenshot waiting for its result's message). */
|
|
79
|
+
holdPart(id: number, until: string): void;
|
|
80
|
+
/** The app's upload identity for a part's file, and whether its bytes are up. */
|
|
81
|
+
setPartFile(id: number, file: string | null, uploaded?: boolean, message?: string | null): void;
|
|
82
|
+
/**
|
|
83
|
+
* Sent: on the app's own message id when it gave one (an uploaded file may not). A card's decide buttons
|
|
84
|
+
* (chat-decide.ts) now ride that message, and any older ones on it stop working; a "What should change?" names it.
|
|
85
|
+
*/
|
|
86
|
+
partSent(id: number, message: string | null, now: Date, attempt?: boolean): void;
|
|
87
|
+
/** Not sent this time: counted, named, and (when the answer was lost rather than refused) counted as uncertain. */
|
|
88
|
+
partFailed(id: number, failure: {
|
|
89
|
+
problem: string;
|
|
90
|
+
until: string;
|
|
91
|
+
uncertain: boolean;
|
|
92
|
+
}, giveUpAfter?: number): void;
|
|
93
|
+
/** One person's replies still waiting to send, and those that could not be delivered (settings show both). */
|
|
94
|
+
partCounts(binding: number): {
|
|
95
|
+
pending: number;
|
|
96
|
+
dropped: number;
|
|
97
|
+
};
|
|
98
|
+
/** Replace a part's content and send it again (a repaint after a tap). */
|
|
99
|
+
repaint(id: number, content: ChatContent): void;
|
|
124
100
|
}
|
|
125
101
|
/** A saved part's content. One that can't be read is a delivery problem that names the field, never a guess. */
|
|
126
102
|
export declare function partContent(payload: string): ChatContent;
|