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
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared chat actions (chat-actions.ts): one schema for each action's data — what the lead proposes and what a saved
|
|
3
|
+
* proposal carries after `prepareSharedAction` has checked and rewritten it — and one for the saved proposal itself.
|
|
4
|
+
* The allowed fields (`CHAT_ACTION_FIELDS`) and the request types are derived from these; what JSON Schema can't say
|
|
5
|
+
* (credentials and control characters in text, each field's length, a zone or option that exists, who may act) runs
|
|
6
|
+
* in `prepareSharedAction` after parsing, with its own named refusal.
|
|
7
|
+
*
|
|
8
|
+
* A flow drawing, trigger or script inside a request is read by its own contract (flows.ts `validateFlowDefinition`,
|
|
9
|
+
* flow-triggers.ts `validateTriggerConfig`, flow-scripts.ts `validateScript`) when the action is prepared, in every
|
|
10
|
+
* vocabulary those accept; here it is only "a value".
|
|
11
|
+
*/
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import { parseContract, readVersioned, versioned } from "./contract.js";
|
|
14
|
+
/** An id the store minted: a positive safe integer. */
|
|
15
|
+
const id = z.int().min(1);
|
|
16
|
+
const words = z.string();
|
|
17
|
+
/** A drawing, trigger or script: read by its own contract when the action is prepared. */
|
|
18
|
+
const nested = z.unknown();
|
|
19
|
+
const skillFile = z.strictObject({ path: z.string(), base64: z.string() });
|
|
20
|
+
/** An object whose keys its own reader knows (a script draft, an action's staleness fence). */
|
|
21
|
+
const anyObject = z.object({}).catchall(z.unknown());
|
|
22
|
+
/**
|
|
23
|
+
* Each action's data, field order as the lead reads it in `get_actions`. Fields a branch ignores or reads through
|
|
24
|
+
* its own validator stay unknown: 0.9.36 accepted null and other non-string values there. Preparation still checks
|
|
25
|
+
* the selected branch and saves the same request bytes. No schema defaults or coercion may change its stamp.
|
|
26
|
+
*/
|
|
27
|
+
export const chatActionRequestSchemas = {
|
|
28
|
+
skill_import: z.strictObject({ repo: words, content: words.optional(), files: z.array(skillFile).optional() }),
|
|
29
|
+
skill_enable: z.strictObject({ repo: words, version: words }),
|
|
30
|
+
skill_disable: z.strictObject({ repo: words, version: words }),
|
|
31
|
+
skill_restore: z.strictObject({ repo: words, restore: id }),
|
|
32
|
+
skill_test: z.strictObject({ repo: words, version: words, sample: words, nonce: words }),
|
|
33
|
+
knowledge_instructions: z.strictObject({ repo: words, instructions: words.optional() }),
|
|
34
|
+
knowledge_save: z.strictObject({ repo: words, title: words, content: words, id: words.optional() }),
|
|
35
|
+
knowledge_remove: z.strictObject({ repo: words, id: words }),
|
|
36
|
+
knowledge_restore: z.strictObject({ repo: words, restore: id }),
|
|
37
|
+
tool_add: z.strictObject({
|
|
38
|
+
repo: words, catalog: z.unknown().optional(), name: z.unknown().optional(), command: z.unknown().optional(), args: z.unknown().optional(),
|
|
39
|
+
url: z.unknown().optional(), secrets: z.unknown().optional(), about: z.unknown().optional(),
|
|
40
|
+
}),
|
|
41
|
+
tool_remove: z.strictObject({ repo: words, name: words }),
|
|
42
|
+
flow_create: z.strictObject({ repo: words, name: words, definition: nested, trigger: nested.optional() }),
|
|
43
|
+
flow_starter: z.strictObject({ repo: words, starter: words }),
|
|
44
|
+
flow_edit: z.strictObject({ flow: id, name: words.optional(), definition: nested.optional() }),
|
|
45
|
+
flow_card_add: z.strictObject({ flow: id, title: words, description: z.unknown().optional(), zone: words.optional() }),
|
|
46
|
+
flow_card_move: z.strictObject({ card: id, zone: words }),
|
|
47
|
+
flow_card_approve: z.strictObject({ card: id, note: words.optional() }),
|
|
48
|
+
flow_card_send_back: z.strictObject({ card: id, note: words.optional() }),
|
|
49
|
+
/** An option by its number (from 1), or a reply that becomes the note. */
|
|
50
|
+
flow_card_choose: z.strictObject({ card: id, choice: z.int().nullable().optional(), note: z.unknown().optional() }),
|
|
51
|
+
flow_card_cancel: z.strictObject({ card: id }),
|
|
52
|
+
flow_card_comment: z.strictObject({ card: id, note: words }),
|
|
53
|
+
flow_card_assign: z.strictObject({ card: id, owner: words.nullable().optional() }),
|
|
54
|
+
flow_card_watch: z.strictObject({ card: id, watching: z.unknown().optional() }),
|
|
55
|
+
flow_script_save: z.strictObject({ repo: words, script: anyObject }),
|
|
56
|
+
flow_trigger_add: z.strictObject({ flow: id, trigger: nested }),
|
|
57
|
+
flow_trigger_pause: z.strictObject({ trigger: id }),
|
|
58
|
+
flow_trigger_resume: z.strictObject({ trigger: id }),
|
|
59
|
+
flow_trigger_remove: z.strictObject({ trigger: id }),
|
|
60
|
+
teammate_create: z.strictObject({ repo: words, template: z.unknown().optional(), name: z.unknown().optional(), soul: z.unknown().optional() }),
|
|
61
|
+
teammate_soul: z.strictObject({ teammate: id, soul: words }),
|
|
62
|
+
teammate_state: z.strictObject({ teammate: id, state: z.enum(["active", "paused", "removed"]) }),
|
|
63
|
+
teammate_note: z.strictObject({ teammate: id, note: words }),
|
|
64
|
+
teammate_answer: z.strictObject({ question: id, choice: z.unknown().optional(), text: z.unknown().optional() }),
|
|
65
|
+
teammate_tools: z.strictObject({
|
|
66
|
+
teammate: id, tool: words, change: z.enum(["grant", "revoke", "rule"]), action: z.unknown().optional(), use: z.unknown().optional(),
|
|
67
|
+
limitField: z.unknown().optional(), limitOver: z.unknown().optional(), undoWith: z.unknown().optional(),
|
|
68
|
+
}),
|
|
69
|
+
teammate_memory: z.strictObject({ teammate: id, memory: id, change: z.enum(["edit", "forget"]), text: z.unknown().optional() }),
|
|
70
|
+
teammate_routine: z.strictObject({ teammate: id, change: z.enum(["add", "remove"]), routine: z.unknown().optional(), schedule: z.unknown().optional(), text: z.unknown().optional() }),
|
|
71
|
+
teammate_undo: z.strictObject({ teammate: id, call: id }),
|
|
72
|
+
kit_setup: z.strictObject({ repo: words, kit: words }),
|
|
73
|
+
decision_record: z.strictObject({ repo: words, claim: words, why: words, supersedes: id.optional(), source: words.optional() }),
|
|
74
|
+
decision_retire: z.strictObject({ repo: words, decision: id, reason: words }),
|
|
75
|
+
/** The line, and (from 1) the line it replaces; 0 or none adds it. */
|
|
76
|
+
lead_about_you: z.strictObject({ line: words, replaces: z.int().min(0).optional() }),
|
|
77
|
+
scope_approve: z.strictObject({ task: words }),
|
|
78
|
+
result_accept: z.strictObject({ task: words, run: id }),
|
|
79
|
+
task_cancel: z.strictObject({ task: words }),
|
|
80
|
+
task_resume: z.strictObject({ task: words, run: id }),
|
|
81
|
+
};
|
|
82
|
+
export const CHAT_ACTION_OPERATIONS = Object.keys(chatActionRequestSchemas);
|
|
83
|
+
/** The fields each action takes, from its schema. */
|
|
84
|
+
export const CHAT_ACTION_FIELDS = Object.fromEntries(CHAT_ACTION_OPERATIONS.map(operation => [operation, Object.keys(chatActionRequestSchemas[operation].shape)]));
|
|
85
|
+
const isOperation = (value) => typeof value === "string" && Object.hasOwn(chatActionRequestSchemas, value);
|
|
86
|
+
/** One action's data, refused by path (`card: required`, `payload: unknown key 'cards' (did you mean card?)`). */
|
|
87
|
+
export function readChatActionRequest(operation, input) {
|
|
88
|
+
return parseContract(chatActionRequestSchemas[operation], input);
|
|
89
|
+
}
|
|
90
|
+
export const SHARED_ACTION_VERSION = 1;
|
|
91
|
+
/**
|
|
92
|
+
* A prepared action as a proposal saves it: the action, its checked request, the project, the card's title and
|
|
93
|
+
* terms, the stamp that proves nothing changed before it is confirmed, and the state the stamp was taken over (each
|
|
94
|
+
* action's own staleness fence, read back only by that action).
|
|
95
|
+
*/
|
|
96
|
+
export const sharedActionSchema = versioned(SHARED_ACTION_VERSION, {
|
|
97
|
+
operation: z.enum(CHAT_ACTION_OPERATIONS),
|
|
98
|
+
request: anyObject,
|
|
99
|
+
repo: z.string(),
|
|
100
|
+
title: z.string(),
|
|
101
|
+
terms: z.array(z.string()),
|
|
102
|
+
stamp: z.string(),
|
|
103
|
+
state: anyObject,
|
|
104
|
+
});
|
|
105
|
+
const SHARED_ACTION_FIELDS = Object.keys(sharedActionSchema.shape).filter(field => field !== "version");
|
|
106
|
+
/**
|
|
107
|
+
* A proposal saved before it carried `version` (every one through 0.9.36), as version 1. Its reader took the fields
|
|
108
|
+
* it knew and passed any others through unread, so this keeps exactly the known fields.
|
|
109
|
+
*/
|
|
110
|
+
export function upgradeUnversionedSharedAction(body) {
|
|
111
|
+
const out = { version: SHARED_ACTION_VERSION };
|
|
112
|
+
for (const field of SHARED_ACTION_FIELDS)
|
|
113
|
+
if (Object.hasOwn(body, field))
|
|
114
|
+
out[field] = body[field];
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
export const SHARED_ACTION_UPGRADES = { 0: upgradeUnversionedSharedAction };
|
|
118
|
+
const under = (prefix, issues) => issues.map(issue => {
|
|
119
|
+
const path = issue.path === "payload" ? prefix : `${prefix}.${issue.path}`;
|
|
120
|
+
return { ...issue, path, line: `${path}${issue.line.slice(issue.path.length)}` };
|
|
121
|
+
});
|
|
122
|
+
/** Legacy readers checked only the envelope. Confirmation still prepares the request again before acting. */
|
|
123
|
+
export function readSharedAction(input) {
|
|
124
|
+
const read = readVersioned(sharedActionSchema, input, SHARED_ACTION_UPGRADES);
|
|
125
|
+
if (!read.ok)
|
|
126
|
+
return read;
|
|
127
|
+
if (!isOperation(read.value.operation))
|
|
128
|
+
return { ok: false, issues: [{ path: "operation", kind: "bad-value", line: "operation: not an action" }] };
|
|
129
|
+
if (!Object.hasOwn(input, "version") || input["version"] === 0)
|
|
130
|
+
return read;
|
|
131
|
+
const request = readChatActionRequest(read.value.operation, read.value.request);
|
|
132
|
+
return request.ok ? read : { ok: false, issues: under("request", request.issues) };
|
|
133
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The saved side of a chat button: the row its one-time token names. A proposal card's buttons in Slack, Discord and
|
|
3
|
+
* Teams (`chat_action`, read with the part and event it rides) and a result's, plan's, failure's or pull request's own
|
|
4
|
+
* buttons (`chat_decide_action`, chat-decide.ts). SQLite's own CHECKs bound them when written; these say what a
|
|
5
|
+
* reader may rely on, so a row that isn't that is refused by column, never read as a guess.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { type ContractResult } from "./contract.js";
|
|
9
|
+
/** A proposal card's button (chat-delivery.ts), with the part's message and the event's binding, channel and thread. */
|
|
10
|
+
export declare const proposalActionRowSchema: z.ZodObject<{
|
|
11
|
+
token: z.ZodString;
|
|
12
|
+
part: z.ZodInt;
|
|
13
|
+
proposal: z.ZodInt;
|
|
14
|
+
phase: z.ZodEnum<{
|
|
15
|
+
cancel: "cancel";
|
|
16
|
+
confirm: "confirm";
|
|
17
|
+
dismiss: "dismiss";
|
|
18
|
+
yes: "yes";
|
|
19
|
+
}>;
|
|
20
|
+
expires: z.ZodString;
|
|
21
|
+
consumed: z.ZodNullable<z.ZodString>;
|
|
22
|
+
message: z.ZodNullable<z.ZodString>;
|
|
23
|
+
binding: z.ZodNullable<z.ZodInt>;
|
|
24
|
+
channel: z.ZodString;
|
|
25
|
+
thread: z.ZodString;
|
|
26
|
+
}, z.core.$strict>;
|
|
27
|
+
export type ProposalActionRow = z.infer<typeof proposalActionRowSchema>;
|
|
28
|
+
export declare function readProposalActionRow(row: unknown): ContractResult<ProposalActionRow>;
|
|
29
|
+
export declare const DECIDE_ACTS: readonly ["accept", "changes", "retry", "approve", "not-now", "merge"];
|
|
30
|
+
export declare const DECIDE_PHASES: readonly ["offer", "yes", "cancel"];
|
|
31
|
+
/** A decide button (chat-decide.ts): who and where it was sent, what it does to which task and run, and the stamp it holds. */
|
|
32
|
+
export declare const decideActionRowSchema: z.ZodObject<{
|
|
33
|
+
token: z.ZodString;
|
|
34
|
+
channel: z.ZodString;
|
|
35
|
+
binding: z.ZodInt;
|
|
36
|
+
chat: z.ZodString;
|
|
37
|
+
message: z.ZodNullable<z.ZodString>;
|
|
38
|
+
act: z.ZodEnum<{
|
|
39
|
+
accept: "accept";
|
|
40
|
+
approve: "approve";
|
|
41
|
+
changes: "changes";
|
|
42
|
+
merge: "merge";
|
|
43
|
+
"not-now": "not-now";
|
|
44
|
+
retry: "retry";
|
|
45
|
+
}>;
|
|
46
|
+
phase: z.ZodEnum<{
|
|
47
|
+
cancel: "cancel";
|
|
48
|
+
offer: "offer";
|
|
49
|
+
yes: "yes";
|
|
50
|
+
}>;
|
|
51
|
+
task_id: z.ZodString;
|
|
52
|
+
run: z.ZodNullable<z.ZodInt>;
|
|
53
|
+
digest: z.ZodString;
|
|
54
|
+
created_at: z.ZodString;
|
|
55
|
+
expires_at: z.ZodString;
|
|
56
|
+
consumed_at: z.ZodNullable<z.ZodString>;
|
|
57
|
+
}, z.core.$strict>;
|
|
58
|
+
export type DecideActionRow = z.infer<typeof decideActionRowSchema>;
|
|
59
|
+
export declare function readDecideActionRow(row: unknown): ContractResult<DecideActionRow>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The saved side of a chat button: the row its one-time token names. A proposal card's buttons in Slack, Discord and
|
|
3
|
+
* Teams (`chat_action`, read with the part and event it rides) and a result's, plan's, failure's or pull request's own
|
|
4
|
+
* buttons (`chat_decide_action`, chat-decide.ts). SQLite's own CHECKs bound them when written; these say what a
|
|
5
|
+
* reader may rely on, so a row that isn't that is refused by column, never read as a guess.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { parseContract } from "./contract.js";
|
|
9
|
+
const when = z.string();
|
|
10
|
+
/** A proposal card's button (chat-delivery.ts), with the part's message and the event's binding, channel and thread. */
|
|
11
|
+
export const proposalActionRowSchema = z.strictObject({
|
|
12
|
+
token: z.string(),
|
|
13
|
+
part: z.int(),
|
|
14
|
+
proposal: z.int(),
|
|
15
|
+
phase: z.enum(["confirm", "dismiss", "yes", "cancel"]),
|
|
16
|
+
expires: when,
|
|
17
|
+
consumed: when.nullable(),
|
|
18
|
+
message: z.string().nullable(),
|
|
19
|
+
binding: z.int().nullable(),
|
|
20
|
+
channel: z.string(),
|
|
21
|
+
thread: z.string(),
|
|
22
|
+
});
|
|
23
|
+
export function readProposalActionRow(row) {
|
|
24
|
+
return parseContract(proposalActionRowSchema, row);
|
|
25
|
+
}
|
|
26
|
+
export const DECIDE_ACTS = ["accept", "changes", "retry", "approve", "not-now", "merge"];
|
|
27
|
+
export const DECIDE_PHASES = ["offer", "yes", "cancel"];
|
|
28
|
+
/** A decide button (chat-decide.ts): who and where it was sent, what it does to which task and run, and the stamp it holds. */
|
|
29
|
+
export const decideActionRowSchema = z.strictObject({
|
|
30
|
+
token: z.string().regex(/^d:[a-f0-9]{24}$/),
|
|
31
|
+
channel: z.string(),
|
|
32
|
+
binding: z.int(),
|
|
33
|
+
chat: z.string(),
|
|
34
|
+
message: z.string().nullable(),
|
|
35
|
+
act: z.enum(DECIDE_ACTS),
|
|
36
|
+
phase: z.enum(DECIDE_PHASES),
|
|
37
|
+
task_id: z.string(),
|
|
38
|
+
run: z.int().nullable(),
|
|
39
|
+
digest: z.string(),
|
|
40
|
+
created_at: when,
|
|
41
|
+
expires_at: when,
|
|
42
|
+
consumed_at: when.nullable(),
|
|
43
|
+
});
|
|
44
|
+
export function readDecideActionRow(row) {
|
|
45
|
+
return parseContract(decideActionRowSchema, row);
|
|
46
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What Slack, Discord and Teams keep between steps (chat-delivery-state.ts): one schema for a received event's body
|
|
3
|
+
* (`chat_event.payload`, by the event's kind) and one for a planned message part (`chat_part.payload`), the message a
|
|
4
|
+
* button rides and the content a delivery repaints. Both are saved with `version`; a body or part saved before they
|
|
5
|
+
* carried one reads as it did then (its known fields; a reader then passed any others by unread).
|
|
6
|
+
*
|
|
7
|
+
* An event body lives only until the event is handled (then it is `{}`); a part lives as long as its message.
|
|
8
|
+
*/
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { type ContractResult } from "./contract.js";
|
|
11
|
+
/** One planned message part, as delivery reads it. */
|
|
12
|
+
export declare const chatContentShape: {
|
|
13
|
+
text: z.ZodString;
|
|
14
|
+
/** Where the part is sent when not the binding's own DM: a room's channel. */
|
|
15
|
+
channel: z.ZodOptional<z.ZodString>;
|
|
16
|
+
proposal: z.ZodOptional<z.ZodInt>;
|
|
17
|
+
image: z.ZodOptional<z.ZodObject<{
|
|
18
|
+
taskId: z.ZodString;
|
|
19
|
+
run: z.ZodInt;
|
|
20
|
+
artifact: z.ZodInt;
|
|
21
|
+
sha256: z.ZodString;
|
|
22
|
+
}, z.core.$strict>>;
|
|
23
|
+
/** A screenshot sent with a result (result-shots.ts): it follows that result's message part, in its thread when
|
|
24
|
+
* the app allows; a refused upload becomes one plain line, and one removed by retention goes quietly. */
|
|
25
|
+
shot: z.ZodOptional<z.ZodObject<{
|
|
26
|
+
follows: z.ZodNullable<z.ZodInt>;
|
|
27
|
+
}, z.core.$strict>>;
|
|
28
|
+
task: z.ZodOptional<z.ZodString>;
|
|
29
|
+
run: z.ZodOptional<z.ZodInt>;
|
|
30
|
+
edit: z.ZodOptional<z.ZodString>;
|
|
31
|
+
phase: z.ZodOptional<z.ZodLiteral<"armed">>;
|
|
32
|
+
link: z.ZodOptional<z.ZodObject<{
|
|
33
|
+
label: z.ZodString;
|
|
34
|
+
path: z.ZodString;
|
|
35
|
+
}, z.core.$strict>>;
|
|
36
|
+
/** More link buttons after `link`: [Look first] beside [Merge] or [Accept and finish]. */
|
|
37
|
+
also: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
38
|
+
label: z.ZodString;
|
|
39
|
+
path: z.ZodString;
|
|
40
|
+
}, z.core.$strict>>>;
|
|
41
|
+
/** A flow decision's buttons ride this part (v88): minted when it is planned. */
|
|
42
|
+
flow: z.ZodOptional<z.ZodObject<{
|
|
43
|
+
card: z.ZodInt;
|
|
44
|
+
entry: z.ZodInt;
|
|
45
|
+
actions: z.ZodArray<z.ZodEnum<{
|
|
46
|
+
approve: "approve";
|
|
47
|
+
edit: "edit";
|
|
48
|
+
"send-back": "send-back";
|
|
49
|
+
}>>;
|
|
50
|
+
}, z.core.$strict>>;
|
|
51
|
+
/** A flow's "Person chooses" buttons ride this part: minted when it is planned. */
|
|
52
|
+
choose: z.ZodOptional<z.ZodObject<{
|
|
53
|
+
card: z.ZodInt;
|
|
54
|
+
entry: z.ZodInt;
|
|
55
|
+
options: z.ZodArray<z.ZodObject<{
|
|
56
|
+
choice: z.ZodInt;
|
|
57
|
+
label: z.ZodString;
|
|
58
|
+
}, z.core.$strict>>;
|
|
59
|
+
}, z.core.$strict>>;
|
|
60
|
+
/** "Use this as your note?" Yes / No about the person's message `held` (an event id), for one choice's visit. */
|
|
61
|
+
note: z.ZodOptional<z.ZodObject<{
|
|
62
|
+
card: z.ZodInt;
|
|
63
|
+
entry: z.ZodInt;
|
|
64
|
+
held: z.ZodString;
|
|
65
|
+
}, z.core.$strict>>;
|
|
66
|
+
/** A teammate's question's buttons ride this part (v93): each option, then one to answer in words (choice null). */
|
|
67
|
+
question: z.ZodOptional<z.ZodObject<{
|
|
68
|
+
id: z.ZodInt;
|
|
69
|
+
choices: z.ZodArray<z.ZodObject<{
|
|
70
|
+
choice: z.ZodNullable<z.ZodString>;
|
|
71
|
+
label: z.ZodString;
|
|
72
|
+
}, z.core.$strict>>;
|
|
73
|
+
}, z.core.$strict>>;
|
|
74
|
+
/** The lead's question to its owner rides this part: its options, then "Something else". */
|
|
75
|
+
ask: z.ZodOptional<z.ZodObject<{
|
|
76
|
+
turn: z.ZodInt;
|
|
77
|
+
options: z.ZodArray<z.ZodString>;
|
|
78
|
+
}, z.core.$strict>>;
|
|
79
|
+
/** The lead's own reply, already shaped (reply-shape.ts): the channel renders its bold anchors and labelled links in its own format. */
|
|
80
|
+
voice: z.ZodOptional<z.ZodLiteral<true>>;
|
|
81
|
+
};
|
|
82
|
+
export declare const chatContentSchema: z.ZodObject<{
|
|
83
|
+
text: z.ZodString;
|
|
84
|
+
channel: z.ZodOptional<z.ZodString>;
|
|
85
|
+
proposal: z.ZodOptional<z.ZodInt>;
|
|
86
|
+
image: z.ZodOptional<z.ZodObject<{
|
|
87
|
+
taskId: z.ZodString;
|
|
88
|
+
run: z.ZodInt;
|
|
89
|
+
artifact: z.ZodInt;
|
|
90
|
+
sha256: z.ZodString;
|
|
91
|
+
}, z.core.$strict>>;
|
|
92
|
+
shot: z.ZodOptional<z.ZodObject<{
|
|
93
|
+
follows: z.ZodNullable<z.ZodInt>;
|
|
94
|
+
}, z.core.$strict>>;
|
|
95
|
+
task: z.ZodOptional<z.ZodString>;
|
|
96
|
+
run: z.ZodOptional<z.ZodInt>;
|
|
97
|
+
edit: z.ZodOptional<z.ZodString>;
|
|
98
|
+
phase: z.ZodOptional<z.ZodLiteral<"armed">>;
|
|
99
|
+
link: z.ZodOptional<z.ZodObject<{
|
|
100
|
+
label: z.ZodString;
|
|
101
|
+
path: z.ZodString;
|
|
102
|
+
}, z.core.$strict>>;
|
|
103
|
+
also: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
104
|
+
label: z.ZodString;
|
|
105
|
+
path: z.ZodString;
|
|
106
|
+
}, z.core.$strict>>>;
|
|
107
|
+
flow: z.ZodOptional<z.ZodObject<{
|
|
108
|
+
card: z.ZodInt;
|
|
109
|
+
entry: z.ZodInt;
|
|
110
|
+
actions: z.ZodArray<z.ZodEnum<{
|
|
111
|
+
approve: "approve";
|
|
112
|
+
edit: "edit";
|
|
113
|
+
"send-back": "send-back";
|
|
114
|
+
}>>;
|
|
115
|
+
}, z.core.$strict>>;
|
|
116
|
+
choose: z.ZodOptional<z.ZodObject<{
|
|
117
|
+
card: z.ZodInt;
|
|
118
|
+
entry: z.ZodInt;
|
|
119
|
+
options: z.ZodArray<z.ZodObject<{
|
|
120
|
+
choice: z.ZodInt;
|
|
121
|
+
label: z.ZodString;
|
|
122
|
+
}, z.core.$strict>>;
|
|
123
|
+
}, z.core.$strict>>;
|
|
124
|
+
note: z.ZodOptional<z.ZodObject<{
|
|
125
|
+
card: z.ZodInt;
|
|
126
|
+
entry: z.ZodInt;
|
|
127
|
+
held: z.ZodString;
|
|
128
|
+
}, z.core.$strict>>;
|
|
129
|
+
question: z.ZodOptional<z.ZodObject<{
|
|
130
|
+
id: z.ZodInt;
|
|
131
|
+
choices: z.ZodArray<z.ZodObject<{
|
|
132
|
+
choice: z.ZodNullable<z.ZodString>;
|
|
133
|
+
label: z.ZodString;
|
|
134
|
+
}, z.core.$strict>>;
|
|
135
|
+
}, z.core.$strict>>;
|
|
136
|
+
ask: z.ZodOptional<z.ZodObject<{
|
|
137
|
+
turn: z.ZodInt;
|
|
138
|
+
options: z.ZodArray<z.ZodString>;
|
|
139
|
+
}, z.core.$strict>>;
|
|
140
|
+
voice: z.ZodOptional<z.ZodLiteral<true>>;
|
|
141
|
+
}, z.core.$strict>;
|
|
142
|
+
export type ChatContent = z.infer<typeof chatContentSchema>;
|
|
143
|
+
export declare const CHAT_PART_VERSION = 1;
|
|
144
|
+
export declare const savedChatPartSchema: z.ZodObject<{
|
|
145
|
+
version: z.ZodLiteral<1>;
|
|
146
|
+
text: z.ZodString;
|
|
147
|
+
channel: z.ZodOptional<z.ZodString>;
|
|
148
|
+
proposal: z.ZodOptional<z.ZodInt>;
|
|
149
|
+
image: z.ZodOptional<z.ZodObject<{
|
|
150
|
+
taskId: z.ZodString;
|
|
151
|
+
run: z.ZodInt;
|
|
152
|
+
artifact: z.ZodInt;
|
|
153
|
+
sha256: z.ZodString;
|
|
154
|
+
}, z.core.$strict>>;
|
|
155
|
+
shot: z.ZodOptional<z.ZodObject<{
|
|
156
|
+
follows: z.ZodNullable<z.ZodInt>;
|
|
157
|
+
}, z.core.$strict>>;
|
|
158
|
+
task: z.ZodOptional<z.ZodString>;
|
|
159
|
+
run: z.ZodOptional<z.ZodInt>;
|
|
160
|
+
edit: z.ZodOptional<z.ZodString>;
|
|
161
|
+
phase: z.ZodOptional<z.ZodLiteral<"armed">>;
|
|
162
|
+
link: z.ZodOptional<z.ZodObject<{
|
|
163
|
+
label: z.ZodString;
|
|
164
|
+
path: z.ZodString;
|
|
165
|
+
}, z.core.$strict>>;
|
|
166
|
+
also: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
167
|
+
label: z.ZodString;
|
|
168
|
+
path: z.ZodString;
|
|
169
|
+
}, z.core.$strict>>>;
|
|
170
|
+
flow: z.ZodOptional<z.ZodObject<{
|
|
171
|
+
card: z.ZodInt;
|
|
172
|
+
entry: z.ZodInt;
|
|
173
|
+
actions: z.ZodArray<z.ZodEnum<{
|
|
174
|
+
approve: "approve";
|
|
175
|
+
edit: "edit";
|
|
176
|
+
"send-back": "send-back";
|
|
177
|
+
}>>;
|
|
178
|
+
}, z.core.$strict>>;
|
|
179
|
+
choose: z.ZodOptional<z.ZodObject<{
|
|
180
|
+
card: z.ZodInt;
|
|
181
|
+
entry: z.ZodInt;
|
|
182
|
+
options: z.ZodArray<z.ZodObject<{
|
|
183
|
+
choice: z.ZodInt;
|
|
184
|
+
label: z.ZodString;
|
|
185
|
+
}, z.core.$strict>>;
|
|
186
|
+
}, z.core.$strict>>;
|
|
187
|
+
note: z.ZodOptional<z.ZodObject<{
|
|
188
|
+
card: z.ZodInt;
|
|
189
|
+
entry: z.ZodInt;
|
|
190
|
+
held: z.ZodString;
|
|
191
|
+
}, z.core.$strict>>;
|
|
192
|
+
question: z.ZodOptional<z.ZodObject<{
|
|
193
|
+
id: z.ZodInt;
|
|
194
|
+
choices: z.ZodArray<z.ZodObject<{
|
|
195
|
+
choice: z.ZodNullable<z.ZodString>;
|
|
196
|
+
label: z.ZodString;
|
|
197
|
+
}, z.core.$strict>>;
|
|
198
|
+
}, z.core.$strict>>;
|
|
199
|
+
ask: z.ZodOptional<z.ZodObject<{
|
|
200
|
+
turn: z.ZodInt;
|
|
201
|
+
options: z.ZodArray<z.ZodString>;
|
|
202
|
+
}, z.core.$strict>>;
|
|
203
|
+
voice: z.ZodOptional<z.ZodLiteral<true>>;
|
|
204
|
+
}, z.core.$strict>;
|
|
205
|
+
/** A saved part's content (`version` is the saved form's, not the content's). */
|
|
206
|
+
export declare function readChatPart(raw: string): ContractResult<ChatContent>;
|
|
207
|
+
/** A part as it is saved. */
|
|
208
|
+
export declare function savedChatPart(content: ChatContent): string;
|
|
209
|
+
export declare const CHAT_EVENT_VERSION = 1;
|
|
210
|
+
/** `pair <code>`: only the code's hash is kept. */
|
|
211
|
+
export declare const chatPairBodySchema: z.ZodObject<{
|
|
212
|
+
version: z.ZodLiteral<1>;
|
|
213
|
+
hash: z.ZodString;
|
|
214
|
+
}, z.core.$strict>;
|
|
215
|
+
/**
|
|
216
|
+
* A message: its words (cut at the lead's limit, `originalLength` saying how long it was), or a plain line to answer
|
|
217
|
+
* with instead (`unsupported`: a file the app can't take). A room's or notice's carrier event has none. `about` is the
|
|
218
|
+
* task its turn is about, once chosen; `lead` is a held message passed to the lead as it was (chat-flow.ts).
|
|
219
|
+
*/
|
|
220
|
+
export declare const chatMessageBodySchema: z.ZodObject<{
|
|
221
|
+
version: z.ZodLiteral<1>;
|
|
222
|
+
text: z.ZodOptional<z.ZodString>;
|
|
223
|
+
originalLength: z.ZodOptional<z.ZodInt>;
|
|
224
|
+
unsupported: z.ZodOptional<z.ZodString>;
|
|
225
|
+
about: z.ZodOptional<z.ZodObject<{
|
|
226
|
+
task: z.ZodString;
|
|
227
|
+
run: z.ZodNullable<z.ZodInt>;
|
|
228
|
+
}, z.core.$strict>>;
|
|
229
|
+
lead: z.ZodOptional<z.ZodLiteral<true>>;
|
|
230
|
+
}, z.core.$strict>;
|
|
231
|
+
/**
|
|
232
|
+
* A button: the one-time token it carried, or, when the app's callback came from the paired person but its data
|
|
233
|
+
* couldn't be read, the path-named line saying why — answered plainly, and nothing is done.
|
|
234
|
+
*/
|
|
235
|
+
export declare const chatTokenBodySchema: z.ZodObject<{
|
|
236
|
+
version: z.ZodLiteral<1>;
|
|
237
|
+
token: z.ZodString;
|
|
238
|
+
}, z.core.$strict>;
|
|
239
|
+
export declare const chatProblemBodySchema: z.ZodObject<{
|
|
240
|
+
version: z.ZodLiteral<1>;
|
|
241
|
+
problem: z.ZodString;
|
|
242
|
+
}, z.core.$strict>;
|
|
243
|
+
export declare const chatActionBodySchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
244
|
+
version: z.ZodLiteral<1>;
|
|
245
|
+
token: z.ZodString;
|
|
246
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
247
|
+
version: z.ZodLiteral<1>;
|
|
248
|
+
problem: z.ZodString;
|
|
249
|
+
}, z.core.$strict>]>;
|
|
250
|
+
/** A notice carries its parts, not a body. */
|
|
251
|
+
export declare const chatNoticeBodySchema: z.ZodObject<{
|
|
252
|
+
version: z.ZodLiteral<1>;
|
|
253
|
+
}, z.core.$strict>;
|
|
254
|
+
export type ChatPairBody = z.infer<typeof chatPairBodySchema>;
|
|
255
|
+
export type ChatMessageBody = z.infer<typeof chatMessageBodySchema>;
|
|
256
|
+
export type ChatActionBody = z.infer<typeof chatActionBodySchema>;
|
|
257
|
+
/** What a writer hands `ChatState.enqueue`: a body of the event's kind, without `version` (it is stamped on saving). */
|
|
258
|
+
export type ChatEventBody = Omit<ChatPairBody, "version"> | Omit<ChatMessageBody, "version"> | {
|
|
259
|
+
token: string;
|
|
260
|
+
} | {
|
|
261
|
+
problem: string;
|
|
262
|
+
} | Record<string, never>;
|
|
263
|
+
export declare function savedChatEventBody(body: ChatEventBody): string;
|
|
264
|
+
/** A pair event's body. */
|
|
265
|
+
export declare function readChatPairBody(raw: string): ContractResult<ChatPairBody>;
|
|
266
|
+
/** A message event's body. */
|
|
267
|
+
export declare function readChatMessageBody(raw: string): ContractResult<ChatMessageBody>;
|
|
268
|
+
/** An action event's body: a token, or the reason its callback couldn't be read. */
|
|
269
|
+
export declare function readChatActionBody(raw: string): ContractResult<ChatActionBody>;
|
|
270
|
+
/** Words held for "Use this as your note?" (`chat_flow_note.words`): the held message's own text and length. */
|
|
271
|
+
export declare const heldWordsSchema: z.ZodObject<{
|
|
272
|
+
text: z.ZodString;
|
|
273
|
+
originalLength: z.ZodOptional<z.ZodInt>;
|
|
274
|
+
}, z.core.$strict>;
|
|
275
|
+
export type HeldWords = z.infer<typeof heldWordsSchema>;
|
|
276
|
+
export declare function readHeldWords(raw: string): ContractResult<HeldWords>;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What Slack, Discord and Teams keep between steps (chat-delivery-state.ts): one schema for a received event's body
|
|
3
|
+
* (`chat_event.payload`, by the event's kind) and one for a planned message part (`chat_part.payload`), the message a
|
|
4
|
+
* button rides and the content a delivery repaints. Both are saved with `version`; a body or part saved before they
|
|
5
|
+
* carried one reads as it did then (its known fields; a reader then passed any others by unread).
|
|
6
|
+
*
|
|
7
|
+
* An event body lives only until the event is handled (then it is `{}`); a part lives as long as its message.
|
|
8
|
+
*/
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { parseContract, readVersioned, versioned } from "./contract.js";
|
|
11
|
+
const id = z.int().min(1);
|
|
12
|
+
const link = z.strictObject({ label: z.string(), path: z.string() });
|
|
13
|
+
/** One planned message part, as delivery reads it. */
|
|
14
|
+
export const chatContentShape = {
|
|
15
|
+
text: z.string(),
|
|
16
|
+
/** Where the part is sent when not the binding's own DM: a room's channel. */
|
|
17
|
+
channel: z.string().optional(),
|
|
18
|
+
proposal: id.optional(),
|
|
19
|
+
image: z.strictObject({ taskId: z.string(), run: z.int(), artifact: z.int(), sha256: z.string() }).optional(),
|
|
20
|
+
/** A screenshot sent with a result (result-shots.ts): it follows that result's message part, in its thread when
|
|
21
|
+
* the app allows; a refused upload becomes one plain line, and one removed by retention goes quietly. */
|
|
22
|
+
shot: z.strictObject({ follows: z.int().nullable() }).optional(),
|
|
23
|
+
task: z.string().optional(),
|
|
24
|
+
run: z.int().optional(),
|
|
25
|
+
edit: z.string().optional(),
|
|
26
|
+
phase: z.literal("armed").optional(),
|
|
27
|
+
link: link.optional(),
|
|
28
|
+
/** More link buttons after `link`: [Look first] beside [Merge] or [Accept and finish]. */
|
|
29
|
+
also: z.array(link).optional(),
|
|
30
|
+
/** A flow decision's buttons ride this part (v88): minted when it is planned. */
|
|
31
|
+
flow: z.strictObject({ card: id, entry: z.int(), actions: z.array(z.enum(["approve", "edit", "send-back"])) }).optional(),
|
|
32
|
+
/** A flow's "Person chooses" buttons ride this part: minted when it is planned. */
|
|
33
|
+
choose: z.strictObject({ card: id, entry: z.int(), options: z.array(z.strictObject({ choice: z.int(), label: z.string() })) }).optional(),
|
|
34
|
+
/** "Use this as your note?" Yes / No about the person's message `held` (an event id), for one choice's visit. */
|
|
35
|
+
note: z.strictObject({ card: id, entry: z.int(), held: z.string() }).optional(),
|
|
36
|
+
/** A teammate's question's buttons ride this part (v93): each option, then one to answer in words (choice null). */
|
|
37
|
+
question: z.strictObject({ id, choices: z.array(z.strictObject({ choice: z.string().nullable(), label: z.string() })) }).optional(),
|
|
38
|
+
/** The lead's question to its owner rides this part: its options, then "Something else". */
|
|
39
|
+
ask: z.strictObject({ turn: id, options: z.array(z.string()) }).optional(),
|
|
40
|
+
/** The lead's own reply, already shaped (reply-shape.ts): the channel renders its bold anchors and labelled links in its own format. */
|
|
41
|
+
voice: z.literal(true).optional(),
|
|
42
|
+
};
|
|
43
|
+
export const chatContentSchema = z.strictObject(chatContentShape);
|
|
44
|
+
export const CHAT_PART_VERSION = 1;
|
|
45
|
+
export const savedChatPartSchema = versioned(CHAT_PART_VERSION, chatContentShape);
|
|
46
|
+
/** A saved body or part from before `version`, as version `version`: the fields its schema knows, nothing else. */
|
|
47
|
+
function knownFields(version, fields) {
|
|
48
|
+
return (body) => {
|
|
49
|
+
const out = { version };
|
|
50
|
+
for (const field of fields)
|
|
51
|
+
if (Object.hasOwn(body, field))
|
|
52
|
+
out[field] = body[field];
|
|
53
|
+
return out;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const PART_UPGRADES = { 0: knownFields(CHAT_PART_VERSION, Object.keys(chatContentShape)) };
|
|
57
|
+
/** JSON text, then the schema; `payload: not JSON` for bytes that aren't. */
|
|
58
|
+
function fromJson(raw, read) {
|
|
59
|
+
let body;
|
|
60
|
+
try {
|
|
61
|
+
body = JSON.parse(raw);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return { ok: false, issues: [{ path: "payload", kind: "invalid", line: "payload: not JSON" }] };
|
|
65
|
+
}
|
|
66
|
+
return read(body);
|
|
67
|
+
}
|
|
68
|
+
/** A saved part's content (`version` is the saved form's, not the content's). */
|
|
69
|
+
export function readChatPart(raw) {
|
|
70
|
+
const read = fromJson(raw, body => readVersioned(savedChatPartSchema, body, PART_UPGRADES));
|
|
71
|
+
if (!read.ok)
|
|
72
|
+
return read;
|
|
73
|
+
const { version: _version, ...content } = read.value;
|
|
74
|
+
return { ok: true, value: content };
|
|
75
|
+
}
|
|
76
|
+
/** A part as it is saved. */
|
|
77
|
+
export function savedChatPart(content) {
|
|
78
|
+
return JSON.stringify({ version: CHAT_PART_VERSION, ...content });
|
|
79
|
+
}
|
|
80
|
+
// ---- event bodies ------------------------------------------------------------------------------------------------
|
|
81
|
+
export const CHAT_EVENT_VERSION = 1;
|
|
82
|
+
/** `pair <code>`: only the code's hash is kept. */
|
|
83
|
+
export const chatPairBodySchema = versioned(CHAT_EVENT_VERSION, { hash: z.string() });
|
|
84
|
+
/**
|
|
85
|
+
* A message: its words (cut at the lead's limit, `originalLength` saying how long it was), or a plain line to answer
|
|
86
|
+
* with instead (`unsupported`: a file the app can't take). A room's or notice's carrier event has none. `about` is the
|
|
87
|
+
* task its turn is about, once chosen; `lead` is a held message passed to the lead as it was (chat-flow.ts).
|
|
88
|
+
*/
|
|
89
|
+
export const chatMessageBodySchema = versioned(CHAT_EVENT_VERSION, {
|
|
90
|
+
text: z.string().optional(),
|
|
91
|
+
originalLength: z.int().min(0).optional(),
|
|
92
|
+
unsupported: z.string().optional(),
|
|
93
|
+
about: z.strictObject({ task: z.string(), run: z.int().nullable() }).optional(),
|
|
94
|
+
lead: z.literal(true).optional(),
|
|
95
|
+
});
|
|
96
|
+
/**
|
|
97
|
+
* A button: the one-time token it carried, or, when the app's callback came from the paired person but its data
|
|
98
|
+
* couldn't be read, the path-named line saying why — answered plainly, and nothing is done.
|
|
99
|
+
*/
|
|
100
|
+
export const chatTokenBodySchema = versioned(CHAT_EVENT_VERSION, { token: z.string().regex(/^[a-f0-9]{32}$/, { error: "must be a Toolroll button token" }) });
|
|
101
|
+
export const chatProblemBodySchema = versioned(CHAT_EVENT_VERSION, { problem: z.string().min(1) });
|
|
102
|
+
export const chatActionBodySchema = z.union([chatTokenBodySchema, chatProblemBodySchema]);
|
|
103
|
+
/** A notice carries its parts, not a body. */
|
|
104
|
+
export const chatNoticeBodySchema = versioned(CHAT_EVENT_VERSION, {});
|
|
105
|
+
export function savedChatEventBody(body) {
|
|
106
|
+
return JSON.stringify({ version: CHAT_EVENT_VERSION, ...body });
|
|
107
|
+
}
|
|
108
|
+
const upgradeBody = (fields) => ({ 0: knownFields(CHAT_EVENT_VERSION, fields) });
|
|
109
|
+
/** A pair event's body. */
|
|
110
|
+
export function readChatPairBody(raw) {
|
|
111
|
+
return fromJson(raw, body => readVersioned(chatPairBodySchema, body, upgradeBody(["hash"])));
|
|
112
|
+
}
|
|
113
|
+
/** A message event's body. */
|
|
114
|
+
export function readChatMessageBody(raw) {
|
|
115
|
+
return fromJson(raw, body => readVersioned(chatMessageBodySchema, body, upgradeBody(Object.keys(chatMessageBodySchema.shape))));
|
|
116
|
+
}
|
|
117
|
+
/** An action event's body: a token, or the reason its callback couldn't be read. */
|
|
118
|
+
export function readChatActionBody(raw) {
|
|
119
|
+
return fromJson(raw, (body) => typeof body === "object" && body !== null && !Array.isArray(body) && Object.hasOwn(body, "problem")
|
|
120
|
+
? readVersioned(chatProblemBodySchema, body, upgradeBody(["problem"]))
|
|
121
|
+
: readVersioned(chatTokenBodySchema, body, upgradeBody(["token"])));
|
|
122
|
+
}
|
|
123
|
+
/** Words held for "Use this as your note?" (`chat_flow_note.words`): the held message's own text and length. */
|
|
124
|
+
export const heldWordsSchema = z.strictObject({ text: z.string(), originalLength: z.int().min(0).optional() });
|
|
125
|
+
export function readHeldWords(raw) {
|
|
126
|
+
return fromJson(raw, body => parseContract(heldWordsSchema, body));
|
|
127
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Slack, Discord and Teams receipt tables (chat-delivery-state.ts keeps their rows). A leaf: it imports nothing, so
|
|
3
|
+
* the store can create these tables without loading the chat state modules that themselves import the store.
|
|
4
|
+
*/
|
|
5
|
+
export declare const chatSchema: (channel: "slack" | "discord" | "teams") => string;
|
|
6
|
+
export declare const chatTables: (channel: "slack" | "discord" | "teams") => string[];
|