toolroll 0.9.36 → 0.9.38
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 +77 -0
- package/dist/assignment-adapters.d.ts +12 -48
- package/dist/assignment-adapters.js +33 -43
- package/dist/browser/THIRD_PARTY_NOTICES.txt +3 -3
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +26 -26
- package/dist/browser-crew.js +5 -0
- package/dist/browser-workspace.d.ts +2 -0
- package/dist/build-review.d.ts +1 -1
- package/dist/build-review.js +2 -1
- package/dist/chat-acceptance.d.ts +1 -1
- package/dist/chat-actions.d.ts +18 -12
- package/dist/chat-actions.js +15 -65
- package/dist/chat-ask.js +5 -5
- package/dist/chat-channel.js +20 -9
- package/dist/chat-decide.d.ts +18 -4
- package/dist/chat-decide.js +31 -11
- package/dist/chat-delivery-state.d.ts +12 -70
- package/dist/chat-delivery-state.js +17 -135
- package/dist/chat-delivery.js +58 -30
- package/dist/chat-flow.js +8 -6
- package/dist/chat-inbox.js +1 -1
- package/dist/coding-context.d.ts +2 -39
- package/dist/coding-context.js +2 -1
- package/dist/coding-handoff.d.ts +6 -25
- package/dist/coding-handoff.js +11 -5
- package/dist/coding-types.d.ts +2 -26
- package/dist/coding-workspace.d.ts +2 -0
- package/dist/coding-workspace.js +50 -14
- package/dist/contracts/acceptance-terms.d.ts +14 -0
- package/dist/contracts/acceptance-terms.js +16 -0
- package/dist/contracts/chat-actions.d.ts +328 -0
- package/dist/contracts/chat-actions.js +133 -0
- package/dist/contracts/chat-callback-rows.d.ts +59 -0
- package/dist/contracts/chat-callback-rows.js +46 -0
- package/dist/contracts/chat-content.d.ts +276 -0
- package/dist/contracts/chat-content.js +127 -0
- package/dist/contracts/chat-tables.d.ts +6 -0
- package/dist/contracts/chat-tables.js +136 -0
- package/dist/contracts/coding-context.d.ts +97 -0
- package/dist/contracts/coding-context.js +60 -0
- package/dist/contracts/coding-handoff.d.ts +44 -0
- package/dist/contracts/coding-handoff.js +47 -0
- package/dist/contracts/coding-workspace.d.ts +165 -0
- package/dist/contracts/coding-workspace.js +71 -0
- package/dist/contracts/contract.d.ts +7 -2
- package/dist/contracts/contract.js +21 -12
- package/dist/contracts/decision.d.ts +45 -0
- package/dist/contracts/decision.js +45 -0
- package/dist/contracts/discord-callback.d.ts +45 -0
- package/dist/contracts/discord-callback.js +34 -0
- package/dist/contracts/flow-card.d.ts +50 -0
- package/dist/contracts/flow-card.js +46 -0
- package/dist/contracts/flow-send.d.ts +119 -0
- package/dist/contracts/flow-send.js +112 -0
- package/dist/contracts/flow.d.ts +5 -0
- package/dist/contracts/flow.js +5 -0
- package/dist/contracts/gateway-tools.d.ts +374 -0
- package/dist/contracts/gateway-tools.js +94 -0
- package/dist/contracts/lead-context.d.ts +165 -0
- package/dist/contracts/lead-context.js +73 -0
- package/dist/contracts/lead-tools.d.ts +1905 -0
- package/dist/contracts/lead-tools.js +265 -0
- package/dist/contracts/memory-pass.d.ts +158 -0
- package/dist/contracts/memory-pass.js +102 -0
- package/dist/contracts/project-knowledge.d.ts +126 -0
- package/dist/contracts/project-knowledge.js +93 -0
- package/dist/contracts/project-memory.d.ts +117 -0
- package/dist/contracts/project-memory.js +51 -0
- package/dist/contracts/project-skills.d.ts +121 -0
- package/dist/contracts/project-skills.js +76 -0
- package/dist/contracts/review-findings.d.ts +46 -0
- package/dist/contracts/review-findings.js +48 -0
- package/dist/contracts/route.d.ts +484 -0
- package/dist/contracts/route.js +128 -0
- package/dist/contracts/scope.d.ts +105 -0
- package/dist/contracts/scope.js +114 -0
- package/dist/contracts/scout-report.d.ts +234 -0
- package/dist/contracts/scout-report.js +141 -0
- package/dist/contracts/slack-callback.d.ts +43 -0
- package/dist/contracts/slack-callback.js +35 -0
- package/dist/contracts/sort-answer.d.ts +83 -0
- package/dist/contracts/sort-answer.js +77 -0
- package/dist/contracts/stage-output.d.ts +127 -0
- package/dist/contracts/stage-output.js +166 -0
- package/dist/contracts/task-sizing.d.ts +24 -0
- package/dist/contracts/task-sizing.js +28 -0
- package/dist/contracts/teammate-turn.d.ts +43 -0
- package/dist/contracts/teammate-turn.js +51 -0
- package/dist/contracts/teams-callback.d.ts +14 -0
- package/dist/contracts/teams-callback.js +14 -0
- package/dist/contracts/telegram-callback.d.ts +120 -0
- package/dist/contracts/telegram-callback.js +97 -0
- package/dist/decision.d.ts +11 -20
- package/dist/decision.js +86 -167
- package/dist/desktop-update.d.ts +1 -1
- package/dist/discord-chat.js +31 -20
- package/dist/flow-engine.js +14 -17
- package/dist/flow-items.d.ts +4 -13
- package/dist/flow-items.js +1 -15
- package/dist/flow-send.d.ts +6 -37
- package/dist/flow-send.js +18 -14
- package/dist/flow-share.js +7 -1
- package/dist/flow-sort.d.ts +7 -26
- package/dist/flow-sort.js +25 -20
- package/dist/flow-steps.js +3 -2
- package/dist/flows.d.ts +2 -4
- package/dist/flows.js +9 -5
- package/dist/invoke.js +4 -4
- package/dist/lead-context.d.ts +4 -2
- package/dist/lead-context.js +24 -17
- package/dist/mate-progress.d.ts +26 -0
- package/dist/mate-progress.js +24 -0
- package/dist/mate-tools.d.ts +4 -0
- package/dist/mate-tools.js +108 -275
- package/dist/mate.js +11 -2
- package/dist/mcp-connect.d.ts +24 -4
- package/dist/mcp-connect.js +78 -15
- package/dist/mcp.js +84 -216
- package/dist/memory-pass.d.ts +17 -42
- package/dist/memory-pass.js +54 -26
- package/dist/phase-routing.d.ts +19 -72
- package/dist/phase-routing.js +34 -164
- package/dist/project-knowledge.d.ts +16 -35
- package/dist/project-knowledge.js +42 -21
- package/dist/project-memory.d.ts +2 -28
- package/dist/project-memory.js +28 -13
- package/dist/project-skills.d.ts +16 -38
- package/dist/project-skills.js +41 -32
- package/dist/project-tools.d.ts +1 -0
- package/dist/project-tools.js +4 -4
- package/dist/provider.d.ts +2 -0
- package/dist/provider.js +4 -16
- package/dist/result-shots.d.ts +1 -1
- package/dist/result-shots.js +2 -1
- package/dist/review-switch.d.ts +3 -8
- package/dist/reviewer.d.ts +8 -12
- package/dist/reviewer.js +11 -35
- package/dist/scope.d.ts +21 -43
- package/dist/scope.js +42 -128
- package/dist/scout-report.d.ts +9 -194
- package/dist/scout-report.js +113 -264
- package/dist/scout.js +15 -11
- package/dist/serve.js +23 -5
- package/dist/setup-guide.d.ts +2 -2
- package/dist/slack-chat.js +26 -20
- package/dist/slack-state.js +2 -1
- package/dist/store.d.ts +9 -32
- package/dist/store.js +18 -15
- package/dist/structured-output.js +2 -1
- package/dist/subscription-chat.d.ts +4 -0
- package/dist/subscription-chat.js +4 -1
- package/dist/task-review-brief.d.ts +1 -7
- package/dist/task-review-brief.js +6 -6
- package/dist/task-sizing.d.ts +3 -27
- package/dist/task-sizing.js +11 -26
- package/dist/task-text.d.ts +0 -5
- package/dist/task-text.js +1 -3
- package/dist/teammate-question.js +7 -5
- package/dist/teammate-work.js +9 -5
- package/dist/teammates.d.ts +13 -64
- package/dist/teammates.js +32 -33
- package/dist/teams-chat.js +20 -11
- package/dist/telegram-flow.d.ts +9 -0
- package/dist/telegram-flow.js +44 -8
- package/dist/telegram-mate.d.ts +4 -14
- package/dist/telegram-mate.js +9 -8
- package/dist/telegram-team.d.ts +2 -19
- package/dist/telegram.d.ts +1 -1
- package/dist/telegram.js +94 -68
- package/dist/text-limits.d.ts +142 -1
- package/dist/text-limits.js +144 -1
- package/dist/tools-ui.d.ts +2 -1
- package/dist/tools-ui.js +7 -4
- package/package.json +17 -12
package/dist/browser-crew.js
CHANGED
|
@@ -38,6 +38,10 @@ export function browserCrewFromIndex(page, conversationId) {
|
|
|
38
38
|
const link = (href) => conversationId && href.startsWith('/chat?') ? href + '&conversation=' + encodeURIComponent(conversationId) : href;
|
|
39
39
|
const rows = page.items.map(summary => {
|
|
40
40
|
const status = summary.status;
|
|
41
|
+
// Keep the server's sentence intact; ignore only empty or repeated badge words.
|
|
42
|
+
const words = (text) => text.trim().replace(/\s+/g, ' ').replace(/[.!?]+$/, '').toLowerCase();
|
|
43
|
+
const detail = ['Waiting', 'Failed', 'Needs you'].includes(status.label)
|
|
44
|
+
&& words(status.detail) !== '' && words(status.detail) !== words(status.label) ? status.detail : undefined;
|
|
41
45
|
const resultHref = summary.resultRunId === null || summary.resultTaskId === null || (summary.resultOutcome !== 'built' && summary.resultOutcome !== 'no-change')
|
|
42
46
|
? null : chatResultHref(summary.resultTaskId, summary.resultRunId);
|
|
43
47
|
const href = chatControlHref('task', summary.rootId);
|
|
@@ -48,6 +52,7 @@ export function browserCrewFromIndex(page, conversationId) {
|
|
|
48
52
|
state: summary.assignmentState, label: status.label, tone: status.tone, href: link(href), resultHref: resultHref === null ? null : link(resultHref),
|
|
49
53
|
action: action === null || actionHref === null ? null : { label: action.label, href: link(actionHref) },
|
|
50
54
|
updatedAt: summary.updatedAt,
|
|
55
|
+
...(detail === undefined ? {} : { detail }),
|
|
51
56
|
...(summary.lead === undefined ? {} : { lead: summary.lead }),
|
|
52
57
|
};
|
|
53
58
|
return { item, rank: status.rank };
|
|
@@ -49,6 +49,8 @@ export type BrowserCrewItem = {
|
|
|
49
49
|
} | null;
|
|
50
50
|
/** When the task family last changed (the index's own sort time), shown as the row's age. */
|
|
51
51
|
updatedAt: string;
|
|
52
|
+
/** The server's status detail, when Waiting, Failed or Needs you adds to its badge. */
|
|
53
|
+
detail?: string;
|
|
52
54
|
/** "<name> is on it.": the person's own lead took it on, by the name they gave it. */
|
|
53
55
|
lead?: string;
|
|
54
56
|
};
|
package/dist/build-review.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Store } from "./store.js";
|
|
2
2
|
import type { Runner } from "./builder.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type BuildFinding } from "./contracts/review-findings.js";
|
|
4
4
|
export { buildReviewOf, buildReviewHeadline, buildReviewLines, findingWords, type BuildReviewView } from "./review-switch.js";
|
|
5
5
|
export declare const AUTOMATIC_REVIEWER = "automatic review";
|
|
6
6
|
/**
|
package/dist/build-review.js
CHANGED
|
@@ -29,6 +29,7 @@ import { runCheckLevel } from "./check-levels.js";
|
|
|
29
29
|
import { requestResultChanges } from "./result-actions.js";
|
|
30
30
|
import { revisionSourceOf } from "./result-review.js";
|
|
31
31
|
import { parseBuildFindings, REVIEW_PATCH_NAME } from "./reviewer.js";
|
|
32
|
+
import { FINDINGS_VERSION } from "./contracts/review-findings.js";
|
|
32
33
|
import { heartbeat as runnerHeartbeat } from "./runner.js";
|
|
33
34
|
import { TELEGRAM_TOKEN_ENVS } from "./names.js";
|
|
34
35
|
import { CLAUDE_LIMITS } from "./scope.js";
|
|
@@ -90,7 +91,7 @@ function queueLocked(store, repo, runId, root, now) {
|
|
|
90
91
|
}
|
|
91
92
|
function settle(store, runId, state, fields, now) {
|
|
92
93
|
store.handle.prepare("UPDATE build_review SET state = ?, findings_json = ?, reason = ?, sent_back_as = ?, finished_at = ? WHERE run = ? AND state = 'pending'")
|
|
93
|
-
.run(state, fields.findings === undefined ? null : JSON.stringify({ version:
|
|
94
|
+
.run(state, fields.findings === undefined ? null : JSON.stringify({ version: FINDINGS_VERSION, findings: fields.findings }), fields.reason ?? null, fields.sentBackAs ?? null, now.toISOString(), runId);
|
|
94
95
|
store.bumpWake();
|
|
95
96
|
}
|
|
96
97
|
/** Only the first automatic send-back in a task family: its revision's HIGH goes to the person. */
|
|
@@ -39,7 +39,7 @@ export declare function readAcceptanceEvidence(store: Store, who: VerifiedApprov
|
|
|
39
39
|
requirement: string | null;
|
|
40
40
|
state: string;
|
|
41
41
|
details: (string | null)[];
|
|
42
|
-
requiredEvidence: readonly
|
|
42
|
+
requiredEvidence: readonly ("changed-path" | "check" | "manual-review" | "screenshot")[];
|
|
43
43
|
reviewer: {
|
|
44
44
|
judgement: import("./proof.js").CriterionJudgementWord;
|
|
45
45
|
note: string | null;
|
package/dist/chat-actions.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type Store } from "./store.js";
|
|
2
2
|
import { type VerifiedApprover } from "./principal.js";
|
|
3
|
+
import { CHAT_ACTION_FIELDS, type SharedAction } from "./contracts/chat-actions.js";
|
|
4
|
+
export { CHAT_ACTION_FIELDS, type SharedAction };
|
|
3
5
|
export declare const CHAT_ACTIONS: {
|
|
4
6
|
readonly skill_import: {
|
|
5
7
|
readonly label: "Add skill";
|
|
@@ -235,20 +237,11 @@ export declare const CHALLENGE_ACTIONS: ReadonlySet<ChatAction>;
|
|
|
235
237
|
/** Actions about the person themselves, in no project: their card carries no project (repo ""). */
|
|
236
238
|
export declare const OWNER_ACTIONS: ReadonlySet<ChatAction>;
|
|
237
239
|
export declare function isChatAction(value: unknown): value is ChatAction;
|
|
238
|
-
export type SharedAction = {
|
|
239
|
-
operation: ChatAction;
|
|
240
|
-
request: Record<string, unknown>;
|
|
241
|
-
repo: string;
|
|
242
|
-
title: string;
|
|
243
|
-
terms: string[];
|
|
244
|
-
stamp: string;
|
|
245
|
-
state: Record<string, unknown>;
|
|
246
|
-
};
|
|
247
|
-
export declare const CHAT_ACTION_FIELDS: Record<ChatAction, readonly string[]>;
|
|
248
240
|
/** No writes, including at preview. Store-derived snapshots are never accepted
|
|
249
241
|
* from a model. Rebuilding the same request before execution catches changes. */
|
|
250
242
|
export declare function prepareSharedAction(store: Store, who: VerifiedApprover, operation: ChatAction, input: Record<string, unknown>, root?: string, now?: Date): SharedAction;
|
|
251
|
-
|
|
243
|
+
/** A saved proposal's action, read by its schema (contracts/chat-actions.ts); null when it can't be read. */
|
|
244
|
+
export declare function sharedActionPayload(value: unknown): SharedAction | null;
|
|
252
245
|
export declare function sharedActionNeedsReview(action: SharedAction): boolean;
|
|
253
246
|
/** Whether a paired phone may confirm this protected action behind its own
|
|
254
247
|
* explicit challenge: the action is on the challenge list, needs no
|
|
@@ -256,7 +249,20 @@ export declare function sharedActionNeedsReview(action: SharedAction): boolean;
|
|
|
256
249
|
export declare function sharedActionAllowsChallenge(action: SharedAction): boolean;
|
|
257
250
|
export declare function sharedActionReviewPath(id: number): string;
|
|
258
251
|
export declare function mintSharedActionReview(store: Store, who: VerifiedApprover, id: number, root: string, now: Date): {
|
|
259
|
-
payload:
|
|
252
|
+
payload: {
|
|
253
|
+
version: 1;
|
|
254
|
+
operation: "decision_record" | "decision_retire" | "flow_card_add" | "flow_card_approve" | "flow_card_assign" | "flow_card_cancel" | "flow_card_choose" | "flow_card_comment" | "flow_card_move" | "flow_card_send_back" | "flow_card_watch" | "flow_create" | "flow_edit" | "flow_script_save" | "flow_starter" | "flow_trigger_add" | "flow_trigger_pause" | "flow_trigger_remove" | "flow_trigger_resume" | "kit_setup" | "knowledge_instructions" | "knowledge_remove" | "knowledge_restore" | "knowledge_save" | "lead_about_you" | "result_accept" | "scope_approve" | "skill_disable" | "skill_enable" | "skill_import" | "skill_restore" | "skill_test" | "task_cancel" | "task_resume" | "teammate_answer" | "teammate_create" | "teammate_memory" | "teammate_note" | "teammate_routine" | "teammate_soul" | "teammate_state" | "teammate_tools" | "teammate_undo" | "tool_add" | "tool_remove";
|
|
255
|
+
request: {
|
|
256
|
+
[x: string]: unknown;
|
|
257
|
+
};
|
|
258
|
+
repo: string;
|
|
259
|
+
title: string;
|
|
260
|
+
terms: string[];
|
|
261
|
+
stamp: string;
|
|
262
|
+
state: {
|
|
263
|
+
[x: string]: unknown;
|
|
264
|
+
};
|
|
265
|
+
};
|
|
260
266
|
nonce: string;
|
|
261
267
|
};
|
|
262
268
|
export type SharedActionOptions = {
|
package/dist/chat-actions.js
CHANGED
|
@@ -35,6 +35,8 @@ import { starterFlowOf, starterOf, startersFor, starterTerms, STARTER_FLOWS, swi
|
|
|
35
35
|
import { answerTeammateQuestion } from "./teammate-work.js";
|
|
36
36
|
import { callWords, checkRule, defaultRule, grantListed, revokeTool, ruleWords, setToolRules } from "./teammate-tools.js";
|
|
37
37
|
import { aboutYouOf, checkAboutYouLine, saveAboutYou, withAboutYouLine } from "./lead-about.js";
|
|
38
|
+
import { CHAT_ACTION_FIELDS, readChatActionRequest, readSharedAction, SHARED_ACTION_VERSION } from "./contracts/chat-actions.js";
|
|
39
|
+
export { CHAT_ACTION_FIELDS };
|
|
38
40
|
/** A tool from the lead's card: a common tool by its id, or the operator's own program or address. */
|
|
39
41
|
function toolSpecFromRequest(input) {
|
|
40
42
|
if (input["catalog"] !== undefined) {
|
|
@@ -118,6 +120,9 @@ export const CHAT_ACTIONS = {
|
|
|
118
120
|
task_cancel: { label: "Cancel task", protected: true, password: false },
|
|
119
121
|
task_resume: { label: "Resume task", protected: true, password: true },
|
|
120
122
|
};
|
|
123
|
+
// Every action has a schema (contracts/chat-actions.ts) and every schema an action: a mismatch fails to compile.
|
|
124
|
+
const SAME_ACTIONS = true;
|
|
125
|
+
void SAME_ACTIONS;
|
|
121
126
|
/** Protected actions a paired phone may confirm behind its own explicit
|
|
122
127
|
* yes/cancel challenge instead of the console's secure screen. Password
|
|
123
128
|
* actions and long or redacted terms never qualify. */
|
|
@@ -128,53 +133,6 @@ export function isChatAction(value) {
|
|
|
128
133
|
return typeof value === "string" && Object.hasOwn(CHAT_ACTIONS, value);
|
|
129
134
|
}
|
|
130
135
|
const hash = (value) => createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
131
|
-
export const CHAT_ACTION_FIELDS = {
|
|
132
|
-
skill_import: ["repo", "content", "files"],
|
|
133
|
-
skill_enable: ["repo", "version"],
|
|
134
|
-
skill_disable: ["repo", "version"],
|
|
135
|
-
skill_restore: ["repo", "restore"],
|
|
136
|
-
skill_test: ["repo", "version", "sample", "nonce"],
|
|
137
|
-
knowledge_instructions: ["repo", "instructions"],
|
|
138
|
-
knowledge_save: ["repo", "title", "content", "id"],
|
|
139
|
-
knowledge_remove: ["repo", "id"],
|
|
140
|
-
knowledge_restore: ["repo", "restore"],
|
|
141
|
-
tool_add: ["repo", "catalog", "name", "command", "args", "url", "secrets", "about"],
|
|
142
|
-
tool_remove: ["repo", "name"],
|
|
143
|
-
flow_create: ["repo", "name", "definition", "trigger"],
|
|
144
|
-
flow_starter: ["repo", "starter"],
|
|
145
|
-
flow_edit: ["flow", "name", "definition"],
|
|
146
|
-
flow_card_add: ["flow", "title", "description", "zone"],
|
|
147
|
-
flow_card_move: ["card", "zone"],
|
|
148
|
-
flow_card_approve: ["card", "note"],
|
|
149
|
-
flow_card_send_back: ["card", "note"],
|
|
150
|
-
flow_card_choose: ["card", "choice", "note"],
|
|
151
|
-
flow_card_cancel: ["card"],
|
|
152
|
-
flow_card_comment: ["card", "note"],
|
|
153
|
-
flow_card_assign: ["card", "owner"],
|
|
154
|
-
flow_card_watch: ["card", "watching"],
|
|
155
|
-
flow_script_save: ["repo", "script"],
|
|
156
|
-
flow_trigger_add: ["flow", "trigger"],
|
|
157
|
-
flow_trigger_pause: ["trigger"],
|
|
158
|
-
flow_trigger_resume: ["trigger"],
|
|
159
|
-
flow_trigger_remove: ["trigger"],
|
|
160
|
-
teammate_create: ["repo", "template", "name", "soul"],
|
|
161
|
-
teammate_soul: ["teammate", "soul"],
|
|
162
|
-
teammate_state: ["teammate", "state"],
|
|
163
|
-
teammate_note: ["teammate", "note"],
|
|
164
|
-
teammate_answer: ["question", "choice", "text"],
|
|
165
|
-
teammate_tools: ["teammate", "tool", "change", "action", "use", "limitField", "limitOver", "undoWith"],
|
|
166
|
-
teammate_memory: ["teammate", "memory", "change", "text"],
|
|
167
|
-
teammate_routine: ["teammate", "change", "routine", "schedule", "text"],
|
|
168
|
-
teammate_undo: ["teammate", "call"],
|
|
169
|
-
kit_setup: ["repo", "kit"],
|
|
170
|
-
decision_record: ["repo", "claim", "why", "supersedes", "source"],
|
|
171
|
-
decision_retire: ["repo", "decision", "reason"],
|
|
172
|
-
lead_about_you: ["line", "replaces"],
|
|
173
|
-
scope_approve: ["task"],
|
|
174
|
-
result_accept: ["task", "run"],
|
|
175
|
-
task_cancel: ["task"],
|
|
176
|
-
task_resume: ["task", "run"],
|
|
177
|
-
};
|
|
178
136
|
const nonceHash = (value) => createHash("sha256").update(value).digest("hex");
|
|
179
137
|
function requireActor(store, who, repo, operation) {
|
|
180
138
|
if (!store.schemaCurrent())
|
|
@@ -277,9 +235,11 @@ const quoted = (value) => `“${value}”`;
|
|
|
277
235
|
export function prepareSharedAction(store, who, operation, input, root, now = new Date()) {
|
|
278
236
|
if (!isChatAction(operation))
|
|
279
237
|
throw Error("Choose an available action.");
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
238
|
+
// The action's own schema: its fields and their kinds, refused by path. The request itself is used as given, so a
|
|
239
|
+
// confirmation rebuilds the same stamp from the same bytes.
|
|
240
|
+
const read = readChatActionRequest(operation, input);
|
|
241
|
+
if (!read.ok)
|
|
242
|
+
throw Error(`This action can't be read — ${read.issues.map(issue => issue.line).join("; ")}.`);
|
|
283
243
|
if (operation === "lead_about_you") {
|
|
284
244
|
// The owner's own note: the card shows the line, and the line it replaces beside it.
|
|
285
245
|
const checked = checkAboutYouLine(input["line"]);
|
|
@@ -300,7 +260,7 @@ export function prepareSharedAction(store, who, operation, input, root, now = ne
|
|
|
300
260
|
const state = replaces === 0 ? {} : { was: lines[replaces - 1] };
|
|
301
261
|
const title = replaces === 0 ? "Remember about you" : "Update what your lead knows about you";
|
|
302
262
|
const stamp = hash({ operation, request, repo: "", state, terms, actor: who.name, generation: who.generation, ceiling: who.ceilingDigest });
|
|
303
|
-
return { operation, request, repo: "", title, terms, stamp, state };
|
|
263
|
+
return { version: SHARED_ACTION_VERSION, operation, request, repo: "", title, terms, stamp, state };
|
|
304
264
|
}
|
|
305
265
|
const task = operation.startsWith("skill_") || operation.startsWith("knowledge_") || operation.startsWith("decision_") || operation.startsWith("tool_") || operation.startsWith("flow_") || operation.startsWith("teammate_") || operation.startsWith("kit_")
|
|
306
266
|
? null
|
|
@@ -965,22 +925,12 @@ export function prepareSharedAction(store, who, operation, input, root, now = ne
|
|
|
965
925
|
generation: who.generation,
|
|
966
926
|
ceiling: who.ceilingDigest,
|
|
967
927
|
});
|
|
968
|
-
return { operation, request, repo, title, terms, stamp, state };
|
|
928
|
+
return { version: SHARED_ACTION_VERSION, operation, request, repo, title, terms, stamp, state };
|
|
969
929
|
}
|
|
930
|
+
/** A saved proposal's action, read by its schema (contracts/chat-actions.ts); null when it can't be read. */
|
|
970
931
|
export function sharedActionPayload(value) {
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
typeof value["repo"] === "string" &&
|
|
974
|
-
typeof value["title"] === "string" &&
|
|
975
|
-
Array.isArray(value["terms"]) &&
|
|
976
|
-
value["terms"].every((t) => typeof t === "string") &&
|
|
977
|
-
value["request"] !== null &&
|
|
978
|
-
typeof value["request"] === "object" &&
|
|
979
|
-
!Array.isArray(value["request"]) &&
|
|
980
|
-
value["state"] !== null &&
|
|
981
|
-
typeof value["state"] === "object"
|
|
982
|
-
? value
|
|
983
|
-
: null;
|
|
932
|
+
const read = readSharedAction(value);
|
|
933
|
+
return read.ok ? read.value : null;
|
|
984
934
|
}
|
|
985
935
|
/** A shortened or redacted preview is not complete consent. This rule lives
|
|
986
936
|
* in the shared door, so a forged transport callback cannot bypass it. */
|
package/dist/chat-ask.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { chatHash } from "./chat-delivery-state.js";
|
|
1
|
+
import { chatHash, partContent, savedChatPart } from "./chat-delivery-state.js";
|
|
2
2
|
import { MATE_ASK_OTHER } from "./store.js";
|
|
3
3
|
/** The question as text with its options, where no buttons are drawn. */
|
|
4
4
|
export function chatAskText(ask) {
|
|
@@ -10,7 +10,7 @@ export function chatAskButtons(state, part, now) {
|
|
|
10
10
|
const row = state.prepare("SELECT p.payload,e.channel,b.channel AS own FROM chat_part p JOIN chat_event e ON e.id=p.event JOIN chat_binding b ON b.id=e.binding WHERE p.id=?").get(part);
|
|
11
11
|
if (row === undefined)
|
|
12
12
|
return [];
|
|
13
|
-
const content =
|
|
13
|
+
const content = partContent(String(row["payload"]));
|
|
14
14
|
if ((content.channel ?? row["channel"]) !== row["own"])
|
|
15
15
|
return [];
|
|
16
16
|
const options = content.ask?.options ?? [];
|
|
@@ -32,9 +32,9 @@ function repaint(state, part, event, line, now) {
|
|
|
32
32
|
state.plan(event.id, [{ text: line }], now);
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
35
|
-
const before =
|
|
35
|
+
const before = partContent(String(row["payload"]));
|
|
36
36
|
const content = { text: `${before.text}\n\n${line}`.slice(0, 3400), edit: event.ts, ...(before.channel === undefined ? {} : { channel: before.channel }) };
|
|
37
|
-
state.prepare("UPDATE chat_part SET payload=?,state='pending',next_at=NULL WHERE id=?").run(
|
|
37
|
+
state.prepare("UPDATE chat_part SET payload=?,state='pending',next_at=NULL WHERE id=?").run(savedChatPart(content), part);
|
|
38
38
|
state.finish(event.id);
|
|
39
39
|
}
|
|
40
40
|
/** A tapped ask button, inside the action's transaction; false when the token isn't one. */
|
|
@@ -84,7 +84,7 @@ export function applyChatAskTap(options, event, binding, token, now) {
|
|
|
84
84
|
state.enqueue({
|
|
85
85
|
id: chatHash(`${state.channel}:ask:${event.id}`).slice(0, 32), installation: event.installation, binding: binding.id, kind: "message",
|
|
86
86
|
channel: event.channel, member: event.member, ts: event.ts, thread: event.thread,
|
|
87
|
-
payload:
|
|
87
|
+
payload: { text: option, originalLength: option.length }, created: now.toISOString(),
|
|
88
88
|
});
|
|
89
89
|
repaint(state, part, event, `You chose: ${option}`, now);
|
|
90
90
|
return true;
|
package/dist/chat-channel.js
CHANGED
|
@@ -273,16 +273,27 @@ export function proposalPreview(store, proposal, repos, channel = null) {
|
|
|
273
273
|
: undefined;
|
|
274
274
|
return path === undefined ? id : projectLabel(path);
|
|
275
275
|
})();
|
|
276
|
+
// Telegram's card leads with the action and what confirming does, then the exact terms; its buttons say the rest.
|
|
277
|
+
const phone = channel === "telegram";
|
|
276
278
|
const card = (headline, body = [], consequence = null) => ({
|
|
277
|
-
text:
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
279
|
+
text: (phone
|
|
280
|
+
? [
|
|
281
|
+
headline,
|
|
282
|
+
...(consequence === null ? [] : [consequence]),
|
|
283
|
+
...(body.length === 0 ? [] : ["", ...body]),
|
|
284
|
+
"",
|
|
285
|
+
"Nothing changes until you confirm.",
|
|
286
|
+
]
|
|
287
|
+
: [
|
|
288
|
+
headline,
|
|
289
|
+
...body,
|
|
290
|
+
...(consequence === null ? [] : ["", consequence]),
|
|
291
|
+
"",
|
|
292
|
+
"Confirm or Dismiss below. Nothing changes until you confirm.",
|
|
293
|
+
]).join("\n"),
|
|
284
294
|
buttons: true,
|
|
285
295
|
});
|
|
296
|
+
const lasting = phone ? " ⚠ can't be undone" : " — IRREVERSIBLE";
|
|
286
297
|
// A handoff's text is origin-free on purpose: it is persisted before the
|
|
287
298
|
// send, and the button or the missing-setup line joins it at send time.
|
|
288
299
|
const handoff = (headline, body = []) => ({
|
|
@@ -361,11 +372,11 @@ export function proposalPreview(store, proposal, repos, channel = null) {
|
|
|
361
372
|
? ["(the decision is gone)"]
|
|
362
373
|
: [
|
|
363
374
|
`Q: ${phoneText(decision.question, 400)}`,
|
|
364
|
-
...decision.options.map((one) => `${one.id === pick ? "→" : " "} ${phoneText(one.label, 120)}${one.reversible ? "" :
|
|
375
|
+
...decision.options.map((one) => `${one.id === pick ? "→" : " "} ${phoneText(one.label, 120)}${one.reversible ? "" : lasting}${one.id === decision.recommendation ? " (the builder recommends this)" : ""}: ${phoneText(one.consequence, 300)}`),
|
|
365
376
|
...(t("rationale") === "" ? [] : [`Why: ${t("rationale", 300)}`]),
|
|
366
377
|
];
|
|
367
378
|
return card(`Answer decision #${String(id ?? "?")} on ${taskName} with "${t("optionLabel", 120) || pick}"`, body, irreversible
|
|
368
|
-
? "⚠ This choice is irreversible. Confirming asks you once more."
|
|
379
|
+
? phone ? "⚠ This choice can't be undone. Confirming asks you once more." : "⚠ This choice is irreversible. Confirming asks you once more."
|
|
369
380
|
: null);
|
|
370
381
|
}
|
|
371
382
|
case "task_action": {
|
package/dist/chat-decide.d.ts
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
|
+
/** Decisions finish in the chat app. A pushed card about a result, a failed task, a plan waiting for approval or a
|
|
2
|
+
* pull request ready to merge carries buttons that act in place: Accept and finish, Request changes, Retry,
|
|
3
|
+
* Approve & start, Merge. Every act that changes something asks twice ("Accept and finish 'X'? [Yes] [Cancel]"),
|
|
4
|
+
* and each button is one opaque token bound to the exact result, receipt, plan or commit the card showed: a card a
|
|
5
|
+
* newer result, a finished task or a changed plan has overtaken says so and acts on nothing.
|
|
6
|
+
*
|
|
7
|
+
* Approving a plan or merging needs the password, which never goes in chat, so they act in chat only under a
|
|
8
|
+
* repository's signed mode that carries the chatApprove term, for the signer's own chat; otherwise, and for a plan
|
|
9
|
+
* that widens permissions, exceeds the mode's budget or touches protected paths, the card keeps its link.
|
|
10
|
+
*
|
|
11
|
+
* Channel-agnostic: Telegram renders these today, and Slack, Discord and Teams reach the same acts through the same
|
|
12
|
+
* tokens (the `channel` column). Every act runs through the door the console uses. */
|
|
13
|
+
import { DECIDE_ACTS } from "./contracts/chat-callback-rows.js";
|
|
1
14
|
import { type ModeTerms } from "./modes.js";
|
|
2
15
|
import { type VerifiedApprover } from "./principal.js";
|
|
3
16
|
import type { Scope } from "./scope.js";
|
|
4
17
|
import type { Store } from "./store.js";
|
|
5
18
|
/** The tokens live in chat_decide_action and the open "What should change?" in chat_decide_prompt (store.ts). */
|
|
6
|
-
export type DecideAct =
|
|
19
|
+
export type DecideAct = (typeof DECIDE_ACTS)[number];
|
|
7
20
|
export type DecideTarget = {
|
|
8
21
|
kind: "result";
|
|
9
22
|
taskId: string;
|
|
@@ -95,11 +108,12 @@ export declare function mergeInChat(store: Store, taskId: string, run: number, a
|
|
|
95
108
|
ok: false;
|
|
96
109
|
why: string;
|
|
97
110
|
};
|
|
98
|
-
/** The plan card: title, goal in two lines, what changes, done when, and what the yes allows.
|
|
99
|
-
|
|
111
|
+
/** The plan card: title, goal in two lines, what changes, done when, and what the yes allows. On Telegram the yes's
|
|
112
|
+
* terms come second, under the title, and the steps are fewer: the full plan is behind Edit. */
|
|
113
|
+
export declare function planCardText(store: Store, taskId: string, scope: Scope, root?: string, channel?: DecideChannel): string;
|
|
100
114
|
/** The acts a card about this target can take in chat right now, or null when none can (the card keeps its own
|
|
101
115
|
* link). Reads only; nothing is minted. */
|
|
102
|
-
export declare function decideOffer(store: Store, target: DecideTarget, who: VerifiedApprover, now: Date, root?: string): DecideOffer | null;
|
|
116
|
+
export declare function decideOffer(store: Store, target: DecideTarget, who: VerifiedApprover, now: Date, root?: string, channel?: DecideChannel): DecideOffer | null;
|
|
103
117
|
/** One fingerprint of what the card offers, without its tokens: a repaint with the same offer keeps its buttons. */
|
|
104
118
|
export declare function offerFingerprint(offer: DecideOffer): string;
|
|
105
119
|
/** The card's buttons, minted now (message unknown until it is sent: `placeDecideTokens` puts them on it). */
|
package/dist/chat-decide.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Channel-agnostic: Telegram renders these today, and Slack, Discord and Teams reach the same acts through the same
|
|
12
12
|
* tokens (the `channel` column). Every act runs through the door the console uses. */
|
|
13
|
+
import { DECIDE_ACTS, readDecideActionRow } from "./contracts/chat-callback-rows.js";
|
|
13
14
|
import { createHash, randomBytes } from "node:crypto";
|
|
14
15
|
import { acceptAndCompleteAsOperator, assignmentOf, owedAcceptance } from "./assignment.js";
|
|
15
16
|
import { chatControlHref, chatResultHref } from "./chat-controls.js";
|
|
@@ -154,8 +155,9 @@ export function mergeInChat(store, taskId, run, approver, now) {
|
|
|
154
155
|
return mode;
|
|
155
156
|
return { ok: true, modeDigest: mode.digest, head: publication.headSha };
|
|
156
157
|
}
|
|
157
|
-
/** The plan card: title, goal in two lines, what changes, done when, and what the yes allows.
|
|
158
|
-
|
|
158
|
+
/** The plan card: title, goal in two lines, what changes, done when, and what the yes allows. On Telegram the yes's
|
|
159
|
+
* terms come second, under the title, and the steps are fewer: the full plan is behind Edit. */
|
|
160
|
+
export function planCardText(store, taskId, scope, root, channel) {
|
|
159
161
|
const ref = store.lookupRef(taskId);
|
|
160
162
|
const artifact = ref === null ? null : store.latestPlanArtifact(ref.id);
|
|
161
163
|
let milestones = [];
|
|
@@ -175,6 +177,19 @@ export function planCardText(store, taskId, scope, root) {
|
|
|
175
177
|
const goal = scope.goal.split("\n").filter(one => one.trim() !== "").slice(0, 2).map(one => phoneText(one, 160));
|
|
176
178
|
const allowing = [permissionPlainWords(scope.profile), scope.budgetMicrousd === null ? "no attempt limit" : `up to ${money(scope.budgetMicrousd)} per attempt`]
|
|
177
179
|
.filter((one) => one !== null);
|
|
180
|
+
if (channel === "telegram") {
|
|
181
|
+
return [
|
|
182
|
+
`Plan ready: ${title(store, taskId)}`,
|
|
183
|
+
`Starting allows: ${allowing.join(" · ")}`,
|
|
184
|
+
"",
|
|
185
|
+
...goal,
|
|
186
|
+
...(scope.touches.length > 0 ? [`Only in: ${scope.touches.slice(0, 6).map(one => phoneText(one, 80)).join(", ")}${scope.touches.length > 6 ? ", …" : ""}`] : ["Any file in the project."]),
|
|
187
|
+
"",
|
|
188
|
+
"Done when:",
|
|
189
|
+
...(scope.acceptance.length > 0 ? bullets(scope.acceptance.map(one => one.statement), 5) : ["You decide when you review the result."]),
|
|
190
|
+
...(milestones.length > 0 ? ["", "Steps:", ...bullets(milestones, 3)] : []),
|
|
191
|
+
].join("\n");
|
|
192
|
+
}
|
|
178
193
|
return [
|
|
179
194
|
`Plan ready: ${title(store, taskId)}`,
|
|
180
195
|
"",
|
|
@@ -193,7 +208,7 @@ export function planCardText(store, taskId, scope, root) {
|
|
|
193
208
|
// ---- what a card offers ---------------------------------------------------------------
|
|
194
209
|
/** The acts a card about this target can take in chat right now, or null when none can (the card keeps its own
|
|
195
210
|
* link). Reads only; nothing is minted. */
|
|
196
|
-
export function decideOffer(store, target, who, now, root) {
|
|
211
|
+
export function decideOffer(store, target, who, now, root, channel) {
|
|
197
212
|
const ref = store.lookupRef(target.taskId);
|
|
198
213
|
if (ref === null || ref.repo === null || !who.repos.includes(ref.repo))
|
|
199
214
|
return null;
|
|
@@ -221,7 +236,7 @@ export function decideOffer(store, target, who, now, root) {
|
|
|
221
236
|
const plan = planInChat(store, target.taskId, who.name, now, root);
|
|
222
237
|
if (!plan.ok)
|
|
223
238
|
return null;
|
|
224
|
-
return { text: planCardText(store, target.taskId, plan.scope, root),
|
|
239
|
+
return { text: planCardText(store, target.taskId, plan.scope, root, channel),
|
|
225
240
|
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 }]] };
|
|
226
241
|
}
|
|
227
242
|
case "merge": {
|
|
@@ -258,11 +273,12 @@ function retryStamp(store, taskId, who) {
|
|
|
258
273
|
export function offerFingerprint(offer) {
|
|
259
274
|
return sha(JSON.stringify(offer));
|
|
260
275
|
}
|
|
261
|
-
function readAction(
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
276
|
+
function readAction(raw) {
|
|
277
|
+
const read = readDecideActionRow(raw);
|
|
278
|
+
if (!read.ok)
|
|
279
|
+
return read;
|
|
280
|
+
const { task_id: taskId, expires_at: expiresAt, consumed_at: consumedAt, created_at: _created, ...row } = read.value;
|
|
281
|
+
return { ok: true, value: { ...row, taskId, expiresAt, consumedAt } };
|
|
266
282
|
}
|
|
267
283
|
function mint(store, seat, message, spec, taskId, phase, now) {
|
|
268
284
|
const token = `d:${randomBytes(12).toString("hex")}`;
|
|
@@ -378,7 +394,11 @@ export function applyDecideTap(store, seat, input) {
|
|
|
378
394
|
const raw = input.token.startsWith("d:") ? store.handle.prepare("SELECT * FROM chat_decide_action WHERE token = ?").get(input.token) : undefined;
|
|
379
395
|
if (raw === undefined)
|
|
380
396
|
return null;
|
|
381
|
-
const
|
|
397
|
+
const read = readAction(raw);
|
|
398
|
+
// A saved button that can't be read acts on nothing, and says why.
|
|
399
|
+
if (!read.ok)
|
|
400
|
+
return { ack: `That button can't be read (${read.issues.map(issue => issue.line).join("; ")}). Nothing was done.`, ignored: true };
|
|
401
|
+
const row = read.value;
|
|
382
402
|
const { now } = input;
|
|
383
403
|
if (row.channel !== seat.channel || row.binding !== seat.binding || row.chat !== seat.chat || (row.message !== null && row.message !== input.message))
|
|
384
404
|
return { ack: "That button isn't for this chat.", ignored: true };
|
|
@@ -408,7 +428,7 @@ export function applyDecideTap(store, seat, input) {
|
|
|
408
428
|
}
|
|
409
429
|
if (row.phase === "cancel") {
|
|
410
430
|
spend();
|
|
411
|
-
const offer = decideOffer(store, target, who, now, input.root);
|
|
431
|
+
const offer = decideOffer(store, target, who, now, input.root, seat.channel);
|
|
412
432
|
if (offer === null)
|
|
413
433
|
return { ack: "Cancelled.", edit: { text: body, rows: linksFor(target), tokens: [] } };
|
|
414
434
|
const minted = mintDecideButtons(store, seat, target, offer, now, input.message);
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { type Store } from "./store.js";
|
|
2
|
+
import { chatSchema, chatTables } from "./contracts/chat-tables.js";
|
|
3
|
+
import { savedChatPart, type ChatContent, type ChatEventBody } from "./contracts/chat-content.js";
|
|
4
|
+
export { savedChatPart, type ChatContent, type ChatEventBody };
|
|
5
|
+
export { chatSchema, chatTables };
|
|
2
6
|
export type ChatIdentity = {
|
|
3
7
|
installation: string;
|
|
4
8
|
team?: string;
|
|
@@ -57,72 +61,6 @@ export type ChatRoom = {
|
|
|
57
61
|
binding: number;
|
|
58
62
|
cursor: number;
|
|
59
63
|
};
|
|
60
|
-
export type ChatContent = {
|
|
61
|
-
text: string;
|
|
62
|
-
/** Where the part is sent when not the binding's own DM: a room's channel. */
|
|
63
|
-
channel?: string;
|
|
64
|
-
proposal?: number;
|
|
65
|
-
image?: {
|
|
66
|
-
taskId: string;
|
|
67
|
-
run: number;
|
|
68
|
-
artifact: number;
|
|
69
|
-
sha256: string;
|
|
70
|
-
};
|
|
71
|
-
/** A screenshot sent with a result (result-shots.ts): it follows that result's message part, in its thread when
|
|
72
|
-
* the app allows; a refused upload becomes one plain line, and one removed by retention goes quietly. */
|
|
73
|
-
shot?: {
|
|
74
|
-
follows: number | null;
|
|
75
|
-
};
|
|
76
|
-
task?: string;
|
|
77
|
-
run?: number;
|
|
78
|
-
edit?: string;
|
|
79
|
-
phase?: "armed";
|
|
80
|
-
link?: {
|
|
81
|
-
label: string;
|
|
82
|
-
path: string;
|
|
83
|
-
};
|
|
84
|
-
/** More link buttons after `link`: [Look first] beside [Merge] or [Accept and finish]. */
|
|
85
|
-
also?: Array<{
|
|
86
|
-
label: string;
|
|
87
|
-
path: string;
|
|
88
|
-
}>;
|
|
89
|
-
/** A flow decision's buttons ride this part (v88): minted when it is planned. */
|
|
90
|
-
flow?: {
|
|
91
|
-
card: number;
|
|
92
|
-
entry: number;
|
|
93
|
-
actions: Array<"approve" | "edit" | "send-back">;
|
|
94
|
-
};
|
|
95
|
-
/** A flow's "Person chooses" buttons ride this part: minted when it is planned. */
|
|
96
|
-
choose?: {
|
|
97
|
-
card: number;
|
|
98
|
-
entry: number;
|
|
99
|
-
options: Array<{
|
|
100
|
-
choice: number;
|
|
101
|
-
label: string;
|
|
102
|
-
}>;
|
|
103
|
-
};
|
|
104
|
-
/** "Use this as your note?" Yes / No about the person's message `held` (an event id), for one choice's visit. */
|
|
105
|
-
note?: {
|
|
106
|
-
card: number;
|
|
107
|
-
entry: number;
|
|
108
|
-
held: string;
|
|
109
|
-
};
|
|
110
|
-
/** A teammate's question's buttons ride this part (v93): each option, then one to answer in words (choice null). */
|
|
111
|
-
question?: {
|
|
112
|
-
id: number;
|
|
113
|
-
choices: Array<{
|
|
114
|
-
choice: string | null;
|
|
115
|
-
label: string;
|
|
116
|
-
}>;
|
|
117
|
-
};
|
|
118
|
-
/** The lead's question to its owner rides this part: its options, then "Something else". */
|
|
119
|
-
ask?: {
|
|
120
|
-
turn: number;
|
|
121
|
-
options: string[];
|
|
122
|
-
};
|
|
123
|
-
/** The lead's own reply, already shaped (reply-shape.ts): the channel renders its bold anchors and labelled links in its own format. */
|
|
124
|
-
voice?: true;
|
|
125
|
-
};
|
|
126
64
|
export declare const chatHash: (text: string) => string;
|
|
127
65
|
export declare class ChatState {
|
|
128
66
|
readonly store: Store;
|
|
@@ -170,7 +108,10 @@ export declare class ChatState {
|
|
|
170
108
|
revoke(installation: string, now?: Date): void;
|
|
171
109
|
pairing(installation: string, approver: string, generation: number, now?: Date): string;
|
|
172
110
|
pair(identity: ChatIdentity, hash: string, member: string, channel: string, now: Date): ChatBinding | null;
|
|
173
|
-
|
|
111
|
+
/** Save a received event with its body (contracts/chat-content.ts), versioned. */
|
|
112
|
+
enqueue(event: Omit<ChatEvent, "session" | "state" | "next_at" | "payload"> & {
|
|
113
|
+
payload: ChatEventBody;
|
|
114
|
+
}): boolean;
|
|
174
115
|
event(id: string): ChatEvent | null;
|
|
175
116
|
next(installation: string, now: Date): ChatEvent | null;
|
|
176
117
|
defer(event: string, problem: string, until: Date): void;
|
|
@@ -181,11 +122,12 @@ export declare class ChatState {
|
|
|
181
122
|
lease(installation: string, owner: string, now: Date): boolean;
|
|
182
123
|
owns(installation: string, owner: string, now?: Date): boolean;
|
|
183
124
|
}
|
|
184
|
-
|
|
185
|
-
export declare
|
|
125
|
+
/** A saved part's content. One that can't be read is a delivery problem that names the field, never a guess. */
|
|
126
|
+
export declare function partContent(payload: string): ChatContent;
|
|
186
127
|
export declare class ChatDeliveryError extends Error {
|
|
187
128
|
readonly code: string;
|
|
188
129
|
readonly retryMs: number;
|
|
189
130
|
readonly uncertain: boolean;
|
|
190
|
-
|
|
131
|
+
readonly permanent: boolean;
|
|
132
|
+
constructor(code: string, retryMs?: number, uncertain?: boolean, permanent?: boolean);
|
|
191
133
|
}
|