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.
Files changed (187) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +55 -80
  3. package/dist/agentconfig.d.ts +11 -36
  4. package/dist/agentconfig.js +3 -62
  5. package/dist/approval-policy.d.ts +1 -1
  6. package/dist/approval-policy.js +1 -1
  7. package/dist/board.d.ts +3 -19
  8. package/dist/board.js +4 -42
  9. package/dist/browser/workspace.js +18 -18
  10. package/dist/browser-workspace.d.ts +2 -1
  11. package/dist/builder.d.ts +9 -40
  12. package/dist/builder.js +50 -257
  13. package/dist/chat-actions.js +3 -14
  14. package/dist/chat-ask.js +8 -8
  15. package/dist/chat-controls.d.ts +0 -4
  16. package/dist/chat-controls.js +0 -1
  17. package/dist/chat-core.d.ts +171 -0
  18. package/dist/chat-core.js +288 -0
  19. package/dist/chat-decide.d.ts +10 -8
  20. package/dist/chat-decide.js +27 -24
  21. package/dist/chat-delivery-state.d.ts +51 -75
  22. package/dist/chat-delivery-state.js +140 -170
  23. package/dist/chat-delivery.d.ts +21 -4
  24. package/dist/chat-delivery.js +313 -195
  25. package/dist/chat-flow.d.ts +29 -1
  26. package/dist/chat-flow.js +24 -50
  27. package/dist/chat-messages.d.ts +319 -0
  28. package/dist/chat-messages.js +841 -0
  29. package/dist/chat-migration.d.ts +30 -0
  30. package/dist/chat-migration.js +802 -0
  31. package/dist/chat-quiet.d.ts +3 -0
  32. package/dist/chat-quiet.js +7 -2
  33. package/dist/chat-task-actions.d.ts +1 -1
  34. package/dist/chat-task-actions.js +0 -2
  35. package/dist/claim.d.ts +79 -244
  36. package/dist/claim.js +688 -1023
  37. package/dist/cli.d.ts +2 -0
  38. package/dist/cli.js +10 -2
  39. package/dist/contracts/chat-content.d.ts +71 -2
  40. package/dist/contracts/chat-content.js +22 -3
  41. package/dist/contracts/chat-tables.d.ts +11 -4
  42. package/dist/contracts/chat-tables.js +165 -87
  43. package/dist/contracts/cli.d.ts +0 -13
  44. package/dist/contracts/cli.js +0 -2
  45. package/dist/contracts/console-api.d.ts +5 -10
  46. package/dist/contracts/console-api.js +4 -10
  47. package/dist/contracts/discord-callback.js +2 -1
  48. package/dist/contracts/flow.d.ts +50 -0
  49. package/dist/contracts/flow.js +26 -1
  50. package/dist/contracts/lead-tools.d.ts +0 -13
  51. package/dist/contracts/lead-tools.js +3 -3
  52. package/dist/contracts/slack-callback.js +3 -2
  53. package/dist/contracts/store-json.d.ts +1 -29
  54. package/dist/contracts/store-json.js +4 -17
  55. package/dist/contracts/teams-callback.js +2 -1
  56. package/dist/converse.d.ts +1 -11
  57. package/dist/converse.js +6 -38
  58. package/dist/coordinator.d.ts +0 -1
  59. package/dist/coordinator.js +3 -5
  60. package/dist/demo.js +16 -26
  61. package/dist/desktop-update-gate.js +0 -1
  62. package/dist/desktop-update.js +1 -3
  63. package/dist/discord-chat.js +19 -38
  64. package/dist/discord-settings.js +4 -13
  65. package/dist/discord.js +4 -14
  66. package/dist/dispatch.d.ts +1 -1
  67. package/dist/dispatch.js +1 -1
  68. package/dist/dispose.d.ts +2 -3
  69. package/dist/dispose.js +17 -92
  70. package/dist/envelope.js +0 -3
  71. package/dist/exec.d.ts +0 -64
  72. package/dist/exec.js +3 -356
  73. package/dist/exhaustion.d.ts +22 -76
  74. package/dist/exhaustion.js +14 -140
  75. package/dist/export.js +8 -8
  76. package/dist/flow-engine.js +1 -1
  77. package/dist/flow-schedule.d.ts +154 -0
  78. package/dist/flow-schedule.js +321 -0
  79. package/dist/flow-share.js +1 -1
  80. package/dist/flow-triggers.d.ts +6 -1
  81. package/dist/flow-triggers.js +65 -10
  82. package/dist/flows-cli.js +8 -4
  83. package/dist/flows-ui.js +2 -0
  84. package/dist/flows.js +1 -1
  85. package/dist/guides.js +9 -29
  86. package/dist/invoke.d.ts +1 -24
  87. package/dist/invoke.js +17 -259
  88. package/dist/lead-status.js +2 -4
  89. package/dist/lead-voice.js +1 -1
  90. package/dist/mate-doors.d.ts +1 -2
  91. package/dist/mate-doors.js +12 -25
  92. package/dist/mate-tools.d.ts +2 -2
  93. package/dist/mate-tools.js +13 -24
  94. package/dist/modes.d.ts +19 -8
  95. package/dist/modes.js +24 -14
  96. package/dist/notification-rows.d.ts +30 -0
  97. package/dist/notification-rows.js +102 -0
  98. package/dist/operate-remote-arguments.js +2 -6
  99. package/dist/operate-remote.js +6 -7
  100. package/dist/operate.d.ts +0 -6
  101. package/dist/operate.js +226 -2248
  102. package/dist/phase-routing.d.ts +13 -23
  103. package/dist/phase-routing.js +13 -35
  104. package/dist/plan-auto.js +2 -2
  105. package/dist/plane-review.js +5 -11
  106. package/dist/planner-source.js +2 -1
  107. package/dist/policy.d.ts +1 -4
  108. package/dist/policy.js +0 -8
  109. package/dist/process-recovery-eligibility.js +2 -3
  110. package/dist/project-delete.js +24 -41
  111. package/dist/proposal.d.ts +4 -24
  112. package/dist/proposal.js +3 -88
  113. package/dist/provider-auth.d.ts +3 -3
  114. package/dist/provider-auth.js +4 -6
  115. package/dist/provider.d.ts +11 -25
  116. package/dist/provider.js +4 -36
  117. package/dist/push.js +1 -1
  118. package/dist/recipe-ui.js +6 -6
  119. package/dist/recipes.d.ts +3 -3
  120. package/dist/recipes.js +27 -23
  121. package/dist/result-shots.js +2 -2
  122. package/dist/retention.js +1 -1
  123. package/dist/run-closeout.d.ts +1 -1
  124. package/dist/run-closeout.js +1 -4
  125. package/dist/scope.d.ts +11 -65
  126. package/dist/scope.js +17 -82
  127. package/dist/serve.js +15 -24
  128. package/dist/server/chat.js +7 -30
  129. package/dist/server/flows.js +11 -10
  130. package/dist/server/guards.js +0 -1
  131. package/dist/server/pages-assets.js +1 -1
  132. package/dist/server/pages.js +0 -2
  133. package/dist/server/people-tokens.js +0 -4
  134. package/dist/server/route-table.d.ts +1 -1
  135. package/dist/server/route-table.js +2 -17
  136. package/dist/server/settings.js +6 -12
  137. package/dist/server/shared.d.ts +28 -212
  138. package/dist/server/shared.js +71 -780
  139. package/dist/server/task-views.js +9 -76
  140. package/dist/server/tasks.d.ts +1 -50
  141. package/dist/server/tasks.js +36 -946
  142. package/dist/setup-guide.d.ts +2 -2
  143. package/dist/slack-api.d.ts +5 -1
  144. package/dist/slack-api.js +2 -2
  145. package/dist/slack-chat.d.ts +9 -6
  146. package/dist/slack-chat.js +29 -53
  147. package/dist/slack-settings.js +6 -15
  148. package/dist/slack.js +6 -16
  149. package/dist/store.d.ts +200 -1702
  150. package/dist/store.js +811 -5439
  151. package/dist/surface.js +10 -24
  152. package/dist/task-composer.d.ts +1 -1
  153. package/dist/task-composer.js +1 -1
  154. package/dist/task-control.d.ts +3 -10
  155. package/dist/task-control.js +2 -7
  156. package/dist/teammate-desk.js +1 -1
  157. package/dist/teammate-question.js +12 -12
  158. package/dist/teammates-ui.js +1 -1
  159. package/dist/teams-chat.js +12 -9
  160. package/dist/teams-settings.js +2 -2
  161. package/dist/teams.js +4 -5
  162. package/dist/telegram-flow.js +5 -5
  163. package/dist/telegram-mate.d.ts +0 -3
  164. package/dist/telegram-mate.js +0 -1
  165. package/dist/telegram.js +4 -4
  166. package/dist/templates.d.ts +11 -7
  167. package/dist/templates.js +12 -9
  168. package/dist/toolroll-update.d.ts +20 -0
  169. package/dist/toolroll-update.js +104 -5
  170. package/dist/work-index.js +2 -6
  171. package/dist/workspace-revision.js +3 -1
  172. package/dist/workspace-ui.js +1 -1
  173. package/package.json +1 -1
  174. package/scripts/recovery-canary.mjs +3 -3
  175. package/dist/contest.d.ts +0 -318
  176. package/dist/contest.js +0 -753
  177. package/dist/held.d.ts +0 -150
  178. package/dist/held.js +0 -799
  179. package/dist/liveness.d.ts +0 -30
  180. package/dist/liveness.js +0 -42
  181. package/dist/pricing.d.ts +0 -45
  182. package/dist/pricing.js +0 -77
  183. package/dist/routine.d.ts +0 -228
  184. package/dist/routine.js +0 -764
  185. package/dist/slack-state.d.ts +0 -13
  186. package/dist/slack-state.js +0 -10
  187. package/dist/supervisor.mjs +0 -297
package/dist/cli.d.ts CHANGED
@@ -80,6 +80,8 @@ export declare function extractOutputFlag(argv: readonly string[]): {
80
80
  error: string;
81
81
  };
82
82
  export declare function main(argv: readonly string[], write?: Write, mainOptions?: MainOptions): Promise<number>;
83
+ /** What every former `toolroll routine …` command says now (v115; kept for this release only). */
84
+ export declare const ROUTINES_MOVED = "Routines are now scheduled flows: see `toolroll flows list` (your routines were moved there).";
83
85
  export declare function partitionRoots(roots: readonly string[], exists: (path: string) => boolean): {
84
86
  present: string[];
85
87
  missing: string[];
package/dist/cli.js CHANGED
@@ -255,7 +255,6 @@ export const OPERATE_COMMANDS = new Set([
255
255
  "publish",
256
256
  "reconcile",
257
257
  "run",
258
- "routine",
259
258
  "flows",
260
259
  "config", "chat", "proposals", "mode", "chat-approval", "people", "tokens", "keys",
261
260
  "setup",
@@ -263,7 +262,6 @@ export const OPERATE_COMMANDS = new Set([
263
262
  "intake",
264
263
  "providers",
265
264
  "template",
266
- "contest",
267
265
  "webhook",
268
266
  "sync",
269
267
  ]);
@@ -383,7 +381,17 @@ export async function main(argv, write = line => console.log(line), mainOptions
383
381
  }
384
382
  return code;
385
383
  }
384
+ /** What every former `toolroll routine …` command says now (v115; kept for this release only). */
385
+ export const ROUTINES_MOVED = "Routines are now scheduled flows: see `toolroll flows list` (your routines were moved there).";
386
+ function routinesMoved(argv, write) {
387
+ write(argv.includes("--json") ? envelopeJson({ ok: true, command: "routine", message: ROUTINES_MOVED }) : ROUTINES_MOVED);
388
+ return 0;
389
+ }
386
390
  async function dispatch(argv, write, mainOptions) {
391
+ // v115: routines became scheduled flows. Every old `routine …` invocation answers with one line pointing there —
392
+ // hidden from help and the contract, before anything remote or local opens, changing nothing.
393
+ if (argv[0] === "routine")
394
+ return routinesMoved(argv, write);
387
395
  // A saved API token sends commands marked remote to the central server, before anything local opens.
388
396
  if (argv[0] === 'health')
389
397
  return runHealthCommand(argv.slice(1), write, mainOptions.team ?? mainOptions.remote);
@@ -1,13 +1,17 @@
1
1
  /**
2
- * What Slack, Discord and Teams keep between steps (chat-delivery-state.ts): one schema for a received event's body
2
+ * What every chat app keeps between steps (chat-delivery-state.ts, and Telegram's store methods): one schema for a received event's body
3
3
  * (`chat_event.payload`, by the event's kind) and one for a planned message part (`chat_part.payload`), the message a
4
4
  * button rides and the content a delivery repaints. Both are saved with `version`; a body or part saved before they
5
5
  * carried one reads as it did then (its known fields; a reader then passed any others by unread).
6
6
  *
7
- * An event body lives only until the event is handled (then it is `{}`); a part lives as long as its message.
7
+ * An event body lives only until the event is handled (then it is `{}`; a Telegram message keeps its words as its
8
+ * record until its chat is unpaired); a part lives as long as its message.
8
9
  */
9
10
  import { z } from "zod";
10
11
  import { type ContractResult } from "./contract.js";
12
+ /** A Toolroll button's one-time token, as every chat app carries it: 32 hex, or `d:` and 24 hex for a decide button
13
+ * (chat-decide.ts: a result's, plan's, failure's or pull request's own buttons). */
14
+ export declare const CHAT_BUTTON_TOKEN: RegExp;
11
15
  /** One planned message part, as delivery reads it. */
12
16
  export declare const chatContentShape: {
13
17
  text: z.ZodString;
@@ -78,6 +82,31 @@ export declare const chatContentShape: {
78
82
  }, z.core.$strict>>;
79
83
  /** The lead's own reply, already shaped (reply-shape.ts): the channel renders its bold anchors and labelled links in its own format. */
80
84
  voice: z.ZodOptional<z.ZodLiteral<true>>;
85
+ /** Telegram: the person's message the first reply part answers. */
86
+ replyTo: z.ZodOptional<z.ZodString>;
87
+ /** Telegram: the part's buttons exactly as sent, their tokens minted with the part (a resend carries the same ones). */
88
+ keyboard: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
89
+ text: z.ZodString;
90
+ callback_data: z.ZodString;
91
+ }, z.core.$loose>>>>;
92
+ /** Telegram: a proposal card, still one after its proposal is gone. */
93
+ card: z.ZodOptional<z.ZodLiteral<true>>;
94
+ /** A result's, plan's, failure's or pull request's own buttons (chat-decide.ts), minted with the part: each an act's
95
+ * one-time token or a link. They ride the message once it is sent. */
96
+ decide: z.ZodOptional<z.ZodObject<{
97
+ rows: z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
98
+ label: z.ZodString;
99
+ token: z.ZodString;
100
+ }, z.core.$strict>, z.ZodObject<{
101
+ label: z.ZodString;
102
+ link: z.ZodObject<{
103
+ label: z.ZodString;
104
+ path: z.ZodString;
105
+ }, z.core.$strict>;
106
+ }, z.core.$strict>]>>>;
107
+ }, z.core.$strict>>;
108
+ /** The "What should change?" this part asks (chat-decide.ts): a reply to its message is the feedback. */
109
+ prompt: z.ZodOptional<z.ZodInt>;
81
110
  };
82
111
  export declare const chatContentSchema: z.ZodObject<{
83
112
  text: z.ZodString;
@@ -138,6 +167,25 @@ export declare const chatContentSchema: z.ZodObject<{
138
167
  options: z.ZodArray<z.ZodString>;
139
168
  }, z.core.$strict>>;
140
169
  voice: z.ZodOptional<z.ZodLiteral<true>>;
170
+ replyTo: z.ZodOptional<z.ZodString>;
171
+ keyboard: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
172
+ text: z.ZodString;
173
+ callback_data: z.ZodString;
174
+ }, z.core.$loose>>>>;
175
+ card: z.ZodOptional<z.ZodLiteral<true>>;
176
+ decide: z.ZodOptional<z.ZodObject<{
177
+ rows: z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
178
+ label: z.ZodString;
179
+ token: z.ZodString;
180
+ }, z.core.$strict>, z.ZodObject<{
181
+ label: z.ZodString;
182
+ link: z.ZodObject<{
183
+ label: z.ZodString;
184
+ path: z.ZodString;
185
+ }, z.core.$strict>;
186
+ }, z.core.$strict>]>>>;
187
+ }, z.core.$strict>>;
188
+ prompt: z.ZodOptional<z.ZodInt>;
141
189
  }, z.core.$strict>;
142
190
  export type ChatContent = z.infer<typeof chatContentSchema>;
143
191
  export declare const CHAT_PART_VERSION = 1;
@@ -201,6 +249,25 @@ export declare const savedChatPartSchema: z.ZodObject<{
201
249
  options: z.ZodArray<z.ZodString>;
202
250
  }, z.core.$strict>>;
203
251
  voice: z.ZodOptional<z.ZodLiteral<true>>;
252
+ replyTo: z.ZodOptional<z.ZodString>;
253
+ keyboard: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
254
+ text: z.ZodString;
255
+ callback_data: z.ZodString;
256
+ }, z.core.$loose>>>>;
257
+ card: z.ZodOptional<z.ZodLiteral<true>>;
258
+ decide: z.ZodOptional<z.ZodObject<{
259
+ rows: z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
260
+ label: z.ZodString;
261
+ token: z.ZodString;
262
+ }, z.core.$strict>, z.ZodObject<{
263
+ label: z.ZodString;
264
+ link: z.ZodObject<{
265
+ label: z.ZodString;
266
+ path: z.ZodString;
267
+ }, z.core.$strict>;
268
+ }, z.core.$strict>]>>>;
269
+ }, z.core.$strict>>;
270
+ prompt: z.ZodOptional<z.ZodInt>;
204
271
  }, z.core.$strict>;
205
272
  /** A saved part's content (`version` is the saved form's, not the content's). */
206
273
  export declare function readChatPart(raw: string): ContractResult<ChatContent>;
@@ -227,6 +294,8 @@ export declare const chatMessageBodySchema: z.ZodObject<{
227
294
  run: z.ZodNullable<z.ZodInt>;
228
295
  }, z.core.$strict>>;
229
296
  lead: z.ZodOptional<z.ZodLiteral<true>>;
297
+ context: z.ZodOptional<z.ZodString>;
298
+ request: z.ZodOptional<z.ZodString>;
230
299
  }, z.core.$strict>;
231
300
  /**
232
301
  * A button: the one-time token it carried, or, when the app's callback came from the paired person but its data
@@ -1,15 +1,19 @@
1
1
  /**
2
- * What Slack, Discord and Teams keep between steps (chat-delivery-state.ts): one schema for a received event's body
2
+ * What every chat app keeps between steps (chat-delivery-state.ts, and Telegram's store methods): one schema for a received event's body
3
3
  * (`chat_event.payload`, by the event's kind) and one for a planned message part (`chat_part.payload`), the message a
4
4
  * button rides and the content a delivery repaints. Both are saved with `version`; a body or part saved before they
5
5
  * carried one reads as it did then (its known fields; a reader then passed any others by unread).
6
6
  *
7
- * An event body lives only until the event is handled (then it is `{}`); a part lives as long as its message.
7
+ * An event body lives only until the event is handled (then it is `{}`; a Telegram message keeps its words as its
8
+ * record until its chat is unpaired); a part lives as long as its message.
8
9
  */
9
10
  import { z } from "zod";
10
11
  import { parseContract, readVersioned, versioned } from "./contract.js";
11
12
  const id = z.int().min(1);
12
13
  const link = z.strictObject({ label: z.string(), path: z.string() });
14
+ /** A Toolroll button's one-time token, as every chat app carries it: 32 hex, or `d:` and 24 hex for a decide button
15
+ * (chat-decide.ts: a result's, plan's, failure's or pull request's own buttons). */
16
+ export const CHAT_BUTTON_TOKEN = /^(?:[a-f0-9]{32}|d:[a-f0-9]{24})$/;
13
17
  /** One planned message part, as delivery reads it. */
14
18
  export const chatContentShape = {
15
19
  text: z.string(),
@@ -39,6 +43,17 @@ export const chatContentShape = {
39
43
  ask: z.strictObject({ turn: id, options: z.array(z.string()) }).optional(),
40
44
  /** The lead's own reply, already shaped (reply-shape.ts): the channel renders its bold anchors and labelled links in its own format. */
41
45
  voice: z.literal(true).optional(),
46
+ /** Telegram: the person's message the first reply part answers. */
47
+ replyTo: z.string().optional(),
48
+ /** Telegram: the part's buttons exactly as sent, their tokens minted with the part (a resend carries the same ones). */
49
+ keyboard: z.array(z.array(z.looseObject({ text: z.string(), callback_data: z.string() }))).optional(),
50
+ /** Telegram: a proposal card, still one after its proposal is gone. */
51
+ card: z.literal(true).optional(),
52
+ /** A result's, plan's, failure's or pull request's own buttons (chat-decide.ts), minted with the part: each an act's
53
+ * one-time token or a link. They ride the message once it is sent. */
54
+ decide: z.strictObject({ rows: z.array(z.array(z.union([z.strictObject({ label: z.string(), token: z.string().regex(CHAT_BUTTON_TOKEN) }), z.strictObject({ label: z.string(), link })]))) }).optional(),
55
+ /** The "What should change?" this part asks (chat-decide.ts): a reply to its message is the feedback. */
56
+ prompt: id.optional(),
42
57
  };
43
58
  export const chatContentSchema = z.strictObject(chatContentShape);
44
59
  export const CHAT_PART_VERSION = 1;
@@ -92,12 +107,16 @@ export const chatMessageBodySchema = versioned(CHAT_EVENT_VERSION, {
92
107
  unsupported: z.string().optional(),
93
108
  about: z.strictObject({ task: z.string(), run: z.int().nullable() }).optional(),
94
109
  lead: z.literal(true).optional(),
110
+ /** Telegram: the server-authored context its turn carries (the exact task or result a reply bound to). */
111
+ context: z.string().optional(),
112
+ /** Telegram: the request identity the lead's engine receipts the turn under. */
113
+ request: z.string().optional(),
95
114
  });
96
115
  /**
97
116
  * A button: the one-time token it carried, or, when the app's callback came from the paired person but its data
98
117
  * couldn't be read, the path-named line saying why — answered plainly, and nothing is done.
99
118
  */
100
- export const chatTokenBodySchema = versioned(CHAT_EVENT_VERSION, { token: z.string().regex(/^[a-f0-9]{32}$/, { error: "must be a Toolroll button token" }) });
119
+ export const chatTokenBodySchema = versioned(CHAT_EVENT_VERSION, { token: z.string().regex(CHAT_BUTTON_TOKEN, { error: "must be a Toolroll button token" }) });
101
120
  export const chatProblemBodySchema = versioned(CHAT_EVENT_VERSION, { problem: z.string().min(1) });
102
121
  export const chatActionBodySchema = z.union([chatTokenBodySchema, chatProblemBodySchema]);
103
122
  /** A notice carries its parts, not a body. */
@@ -1,6 +1,13 @@
1
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.
2
+ * The one set of chat tables every chat app shares (v116): Telegram, Slack, Discord and Teams keep their pairings,
3
+ * received events, planned message parts, buttons, rooms and delivery receipts here, each row keyed by `provider`.
4
+ * A row's identity is (provider, id): ids carried over from the per-app tables keep their numbers, and nothing infers
5
+ * the app from an id. A leaf: it imports nothing, so the store creates these tables without loading the chat modules.
4
6
  */
5
- export declare const chatSchema: (channel: "slack" | "discord" | "teams") => string;
6
- export declare const chatTables: (channel: "slack" | "discord" | "teams") => string[];
7
+ export declare const CHAT_PROVIDERS: readonly ["telegram", "slack", "discord", "teams"];
8
+ export type ChatProvider = (typeof CHAT_PROVIDERS)[number];
9
+ export declare const isChatProvider: (value: unknown) => value is ChatProvider;
10
+ export declare const CHAT_SCHEMA: string;
11
+ /** Every shared chat table, parents before children. */
12
+ export declare const CHAT_TABLES: readonly ["chat_binding", "chat_pair", "chat_event", "chat_part", "chat_action", "chat_progress", "chat_runtime", "chat_room", "chat_meta", "chat_flow_action", "chat_flow_prompt", "chat_flow_choice", "chat_flow_note", "chat_question_action", "chat_question_prompt", "chat_ask_action", "chat_message_ref", "chat_note_draft", "chat_digest"];
13
+ export type ChatTable = (typeof CHAT_TABLES)[number];
@@ -1,129 +1,197 @@
1
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.
2
+ * The one set of chat tables every chat app shares (v116): Telegram, Slack, Discord and Teams keep their pairings,
3
+ * received events, planned message parts, buttons, rooms and delivery receipts here, each row keyed by `provider`.
4
+ * A row's identity is (provider, id): ids carried over from the per-app tables keep their numbers, and nothing infers
5
+ * the app from an id. A leaf: it imports nothing, so the store creates these tables without loading the chat modules.
4
6
  */
5
- const CHAT_SCHEMA = `
7
+ export const CHAT_PROVIDERS = ["telegram", "slack", "discord", "teams"];
8
+ export const isChatProvider = (value) => CHAT_PROVIDERS.includes(value);
9
+ const PROVIDER = `provider TEXT NOT NULL CHECK (provider IN (${CHAT_PROVIDERS.map(one => `'${one}'`).join(",")}))`;
10
+ export const CHAT_SCHEMA = `
11
+ -- One person's chat in one app, speaking as one approver generation. Revoked, never deleted.
12
+ -- installation: the bot (Telegram) or workspace app; member: the app's user id; channel: their private chat.
6
13
  CREATE TABLE IF NOT EXISTS chat_binding (
7
- id INTEGER PRIMARY KEY AUTOINCREMENT, installation TEXT NOT NULL,
8
- team TEXT NOT NULL, app TEXT NOT NULL, member TEXT NOT NULL, channel TEXT NOT NULL,
9
- approver TEXT NOT NULL, generation INTEGER NOT NULL, created TEXT NOT NULL, revoked TEXT
10
- );
11
- CREATE UNIQUE INDEX IF NOT EXISTS chat_one_binding_member ON chat_binding(installation, member) WHERE revoked IS NULL;
14
+ ${PROVIDER}, id INTEGER NOT NULL, installation TEXT NOT NULL, team TEXT NOT NULL, app TEXT NOT NULL,
15
+ member TEXT NOT NULL, channel TEXT NOT NULL, approver TEXT NOT NULL, generation INTEGER NOT NULL,
16
+ created TEXT NOT NULL, created_by TEXT, pair_event TEXT, revoked TEXT, revoked_by TEXT,
17
+ PRIMARY KEY (provider, id)
18
+ );
19
+ CREATE UNIQUE INDEX IF NOT EXISTS chat_binding_live ON chat_binding(provider, installation, member) WHERE revoked IS NULL;
20
+ CREATE INDEX IF NOT EXISTS chat_binding_approver ON chat_binding(provider, approver) WHERE revoked IS NULL;
21
+ -- One-time pairing codes, by hash. installation NULL: any installation of the app (a Telegram code works on its bot).
12
22
  CREATE TABLE IF NOT EXISTS chat_pair (
13
- hash TEXT PRIMARY KEY, installation TEXT NOT NULL, approver TEXT NOT NULL,
14
- generation INTEGER NOT NULL, expires TEXT NOT NULL, consumed TEXT
23
+ ${PROVIDER}, hash TEXT NOT NULL, installation TEXT, approver TEXT NOT NULL, generation INTEGER NOT NULL,
24
+ created TEXT, created_by TEXT, expires TEXT NOT NULL, consumed TEXT, consumed_channel TEXT, consumed_member TEXT, consumed_event TEXT,
25
+ PRIMARY KEY (provider, hash)
15
26
  );
27
+ -- Everything an app sent us, once each (the key is the idempotency). kind 'update' is an applied (or pushed and
28
+ -- waiting) Telegram update; 'message' is a message the lead answers, queued to running to done or failed.
29
+ -- session: the lead session its turn was dispatched under, a pointer to its receipt (never an authority).
16
30
  CREATE TABLE IF NOT EXISTS chat_event (
17
- id TEXT PRIMARY KEY, installation TEXT NOT NULL, binding INTEGER REFERENCES chat_binding(id),
18
- kind TEXT NOT NULL CHECK(kind IN ('message','action','pair','notice')),
31
+ ${PROVIDER}, id TEXT NOT NULL, installation TEXT NOT NULL, binding INTEGER,
32
+ kind TEXT NOT NULL CHECK (kind IN ('message','action','pair','notice','update')),
19
33
  channel TEXT NOT NULL, member TEXT NOT NULL, ts TEXT NOT NULL, thread TEXT NOT NULL,
20
- payload TEXT NOT NULL, created TEXT NOT NULL,
21
- session INTEGER REFERENCES mate_session(id), state TEXT NOT NULL DEFAULT 'queued' CHECK(state IN ('queued','done','dropped')),
22
- next_at TEXT, problem TEXT
23
- );
24
- CREATE INDEX IF NOT EXISTS chat_pending_event ON chat_event(installation,state,next_at);
34
+ payload TEXT NOT NULL, created TEXT NOT NULL, session INTEGER,
35
+ state TEXT NOT NULL DEFAULT 'queued' CHECK (state IN ('queued','running','done','failed','dropped')),
36
+ next_at TEXT, problem TEXT, claim_owner TEXT, claim_until TEXT, attempts INTEGER NOT NULL DEFAULT 0,
37
+ turn INTEGER, started TEXT, finished TEXT, result TEXT, reply TEXT,
38
+ PRIMARY KEY (provider, id),
39
+ FOREIGN KEY (provider, binding) REFERENCES chat_binding(provider, id)
40
+ );
41
+ CREATE INDEX IF NOT EXISTS chat_event_pending ON chat_event(provider, installation, state, next_at);
42
+ CREATE INDEX IF NOT EXISTS chat_event_binding ON chat_event(provider, binding, state);
43
+ -- What we send, planned before any send and marked sent only on the app's own message id.
25
44
  CREATE TABLE IF NOT EXISTS chat_part (
26
- id INTEGER PRIMARY KEY AUTOINCREMENT, event TEXT NOT NULL REFERENCES chat_event(id), ordinal INTEGER NOT NULL,
27
- payload TEXT NOT NULL, state TEXT NOT NULL DEFAULT 'pending' CHECK(state IN ('pending','sent','dropped')),
45
+ ${PROVIDER}, id INTEGER NOT NULL, event TEXT NOT NULL, ordinal INTEGER NOT NULL, payload TEXT NOT NULL,
46
+ state TEXT NOT NULL DEFAULT 'pending' CHECK (state IN ('pending','sent','dropped')),
28
47
  message TEXT, file TEXT, uploaded INTEGER NOT NULL DEFAULT 0, attempts INTEGER NOT NULL DEFAULT 0,
29
- uncertain INTEGER NOT NULL DEFAULT 0, next_at TEXT, problem TEXT, created TEXT NOT NULL,
30
- UNIQUE(event,ordinal)
31
- );
48
+ uncertain INTEGER NOT NULL DEFAULT 0, next_at TEXT, problem TEXT, created TEXT NOT NULL, sent TEXT,
49
+ PRIMARY KEY (provider, id), UNIQUE (provider, event, ordinal),
50
+ FOREIGN KEY (provider, event) REFERENCES chat_event(provider, id)
51
+ );
52
+ CREATE INDEX IF NOT EXISTS chat_part_pending ON chat_part(provider, state, next_at);
53
+ CREATE INDEX IF NOT EXISTS chat_part_message ON chat_part(provider, message);
54
+ -- A lead proposal's buttons (proposal), or a builder decision's (decision, option_id), one opaque token each.
55
+ -- A button rides its planned part, or (Telegram) the binding, chat and message it was placed on.
32
56
  CREATE TABLE IF NOT EXISTS chat_action (
33
- token TEXT PRIMARY KEY, part INTEGER NOT NULL REFERENCES chat_part(id),
34
- proposal INTEGER NOT NULL REFERENCES mate_proposal(id), phase TEXT NOT NULL CHECK(phase IN ('confirm','dismiss','yes','cancel')),
35
- expires TEXT NOT NULL, consumed TEXT
36
- );
57
+ ${PROVIDER}, token TEXT NOT NULL, part INTEGER, binding INTEGER, chat TEXT, message TEXT,
58
+ proposal INTEGER REFERENCES mate_proposal(id) ON DELETE CASCADE,
59
+ decision INTEGER REFERENCES decision(id) ON DELETE CASCADE, option_id TEXT, note_digest TEXT,
60
+ phase TEXT NOT NULL CHECK (phase IN ('confirm','dismiss','yes','cancel','choose')),
61
+ created TEXT, expires TEXT, consumed TEXT,
62
+ PRIMARY KEY (provider, token),
63
+ CHECK ((proposal IS NULL) <> (decision IS NULL)),
64
+ FOREIGN KEY (provider, part) REFERENCES chat_part(provider, id)
65
+ );
66
+ CREATE INDEX IF NOT EXISTS chat_action_part ON chat_action(provider, part);
67
+ CREATE INDEX IF NOT EXISTS chat_action_proposal ON chat_action(proposal);
68
+ CREATE INDEX IF NOT EXISTS chat_action_decision ON chat_action(decision);
37
69
  CREATE TABLE IF NOT EXISTS chat_progress (
38
- binding INTEGER NOT NULL REFERENCES chat_binding(id), run INTEGER NOT NULL REFERENCES run(id),
39
- part INTEGER NOT NULL REFERENCES chat_part(id), digest TEXT NOT NULL,
40
- PRIMARY KEY(binding,run)
70
+ ${PROVIDER}, binding INTEGER NOT NULL, run INTEGER NOT NULL REFERENCES run(id), part INTEGER NOT NULL, digest TEXT NOT NULL,
71
+ PRIMARY KEY (provider, binding, run),
72
+ FOREIGN KEY (provider, binding) REFERENCES chat_binding(provider, id),
73
+ FOREIGN KEY (provider, part) REFERENCES chat_part(provider, id)
41
74
  );
75
+ -- One live worker per installation (lease, its generation and the poll cursor), its connection and retry state,
76
+ -- the newest notification its notices started from, and where (Telegram) pushes updates.
42
77
  CREATE TABLE IF NOT EXISTS chat_runtime (
43
- installation TEXT PRIMARY KEY, owner TEXT, lease_until TEXT, connected TEXT,
44
- problem TEXT, retry_at TEXT, notification INTEGER NOT NULL DEFAULT 0
78
+ ${PROVIDER}, installation TEXT NOT NULL, owner TEXT, lease_until TEXT, generation INTEGER NOT NULL DEFAULT 0,
79
+ cursor INTEGER NOT NULL DEFAULT 0, heartbeat TEXT, connected TEXT, problem TEXT, retry_at TEXT,
80
+ notification INTEGER NOT NULL DEFAULT 0, push_url TEXT, push_at TEXT, push_problem TEXT,
81
+ PRIMARY KEY (provider, installation)
45
82
  );
46
- `;
47
- /** v73: a chat's place in the shared team conversations — a channel follows one
48
- * conversation, a DM may select one. Rows are revoked, never deleted. */
49
- const CHAT_ROOM_SCHEMA = `
83
+ -- A chat's place in the shared team conversations: a group follows one conversation, a private chat may select one.
50
84
  CREATE TABLE IF NOT EXISTS chat_room (
51
- id INTEGER PRIMARY KEY AUTOINCREMENT, installation TEXT NOT NULL, chat TEXT NOT NULL,
52
- kind TEXT NOT NULL CHECK(kind IN ('group','private')), conversation TEXT NOT NULL,
53
- binding INTEGER NOT NULL REFERENCES chat_binding(id),
54
- bound_by TEXT NOT NULL, bound TEXT NOT NULL, cursor INTEGER NOT NULL DEFAULT 0,
55
- revoked TEXT, revoked_by TEXT
56
- );
57
- CREATE UNIQUE INDEX IF NOT EXISTS chat_room_live ON chat_room(installation, chat) WHERE revoked IS NULL;
58
- CREATE UNIQUE INDEX IF NOT EXISTS chat_room_group ON chat_room(conversation) WHERE revoked IS NULL AND kind='group';
85
+ ${PROVIDER}, id INTEGER NOT NULL, installation TEXT NOT NULL, chat TEXT NOT NULL,
86
+ kind TEXT NOT NULL CHECK (kind IN ('group','private')), conversation TEXT NOT NULL REFERENCES team_conversation(id),
87
+ binding INTEGER NOT NULL, bound_by TEXT NOT NULL, bound TEXT NOT NULL, cursor INTEGER NOT NULL DEFAULT 0,
88
+ revoked TEXT, revoked_by TEXT,
89
+ PRIMARY KEY (provider, id),
90
+ FOREIGN KEY (provider, binding) REFERENCES chat_binding(provider, id)
91
+ );
92
+ CREATE UNIQUE INDEX IF NOT EXISTS chat_room_live ON chat_room(provider, installation, chat) WHERE revoked IS NULL;
93
+ CREATE UNIQUE INDEX IF NOT EXISTS chat_room_group ON chat_room(provider, conversation) WHERE revoked IS NULL AND kind = 'group';
94
+ -- Small transport facts an app must remember per chat (a Teams service URL). Never credentials.
59
95
  CREATE TABLE IF NOT EXISTS chat_meta (
60
- installation TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL, updated TEXT NOT NULL,
61
- PRIMARY KEY(installation, key)
96
+ ${PROVIDER}, installation TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL, updated TEXT NOT NULL,
97
+ PRIMARY KEY (provider, installation, key)
62
98
  );
63
- `;
64
- /** v88: flow decisions in the chat app, as on Telegram (telegram-flow.ts).
65
- * A button is one opaque token for one visit (card, entry) of one card, on
66
- * the part it rides; Edit and Send back open a prompt the person's next
67
- * message in their DM answers. */
68
- const CHAT_FLOW_SCHEMA = `
99
+ -- A flow decision's buttons (Approve, Edit, Send back, and the Yes or Cancel Approve arms) for one visit of one card.
69
100
  CREATE TABLE IF NOT EXISTS chat_flow_action (
70
- token TEXT PRIMARY KEY, part INTEGER NOT NULL REFERENCES chat_part(id),
101
+ ${PROVIDER}, token TEXT NOT NULL, part INTEGER, binding INTEGER, chat TEXT, message TEXT,
71
102
  card INTEGER NOT NULL REFERENCES flow_card(id), entry INTEGER NOT NULL,
72
- action TEXT NOT NULL CHECK(action IN ('approve','edit','send-back')), expires TEXT NOT NULL, consumed TEXT
103
+ action TEXT NOT NULL CHECK (action IN ('approve','edit','send-back','yes','cancel')),
104
+ created TEXT, expires TEXT NOT NULL, consumed TEXT,
105
+ PRIMARY KEY (provider, token),
106
+ FOREIGN KEY (provider, part) REFERENCES chat_part(provider, id)
73
107
  );
74
108
  CREATE INDEX IF NOT EXISTS chat_flow_action_visit ON chat_flow_action(card, entry);
109
+ CREATE INDEX IF NOT EXISTS chat_flow_action_part ON chat_flow_action(provider, part);
110
+ -- Edit and Send back wait for the person's next message (or, on Telegram, a reply to the prompt message).
75
111
  CREATE TABLE IF NOT EXISTS chat_flow_prompt (
76
- id INTEGER PRIMARY KEY AUTOINCREMENT, binding INTEGER NOT NULL REFERENCES chat_binding(id),
112
+ ${PROVIDER}, id INTEGER NOT NULL, binding INTEGER NOT NULL, chat TEXT, message TEXT,
77
113
  card INTEGER NOT NULL REFERENCES flow_card(id), entry INTEGER NOT NULL,
78
- mode TEXT NOT NULL CHECK(mode IN ('edit','send-back')), created TEXT NOT NULL, expires TEXT NOT NULL, consumed TEXT
114
+ mode TEXT NOT NULL CHECK (mode IN ('edit','send-back')), created TEXT NOT NULL, expires TEXT NOT NULL, consumed TEXT,
115
+ PRIMARY KEY (provider, id)
79
116
  );
80
- CREATE INDEX IF NOT EXISTS chat_flow_prompt_open ON chat_flow_prompt(binding, consumed);
81
- `;
82
- /** A flow's "Person chooses" zone in the chat app (flow-send.ts): one button per option (choice is its index, label its words
83
- * when sent) for one visit of one card, on the part it rides. A reply in that notice's thread is the note; chat_flow_note
84
- * is "Use this as your note?" Yes / No about one other message (held: its event; words: what it said, kept only until
85
- * answered, as an event's own payload is cleared once it is handled), asked once per visit.
86
- * Never in CHAT_TABLES: an older database has no such table until this schema creates it. */
87
- const CHAT_FLOW_CHOICE_SCHEMA = `
117
+ CREATE INDEX IF NOT EXISTS chat_flow_prompt_open ON chat_flow_prompt(provider, binding, consumed);
118
+ CREATE UNIQUE INDEX IF NOT EXISTS chat_flow_prompt_message ON chat_flow_prompt(provider, chat, message) WHERE message IS NOT NULL;
119
+ -- A flow's "Person chooses" buttons: one per option (choice is its index, label its words when sent).
88
120
  CREATE TABLE IF NOT EXISTS chat_flow_choice (
89
- token TEXT PRIMARY KEY, part INTEGER NOT NULL REFERENCES chat_part(id),
121
+ ${PROVIDER}, token TEXT NOT NULL, part INTEGER, binding INTEGER, chat TEXT, message TEXT,
90
122
  card INTEGER NOT NULL REFERENCES flow_card(id), entry INTEGER NOT NULL,
91
- choice INTEGER NOT NULL, label TEXT NOT NULL, expires TEXT NOT NULL, consumed TEXT
123
+ choice INTEGER NOT NULL, label TEXT NOT NULL, created TEXT, expires TEXT NOT NULL, consumed TEXT,
124
+ PRIMARY KEY (provider, token),
125
+ FOREIGN KEY (provider, part) REFERENCES chat_part(provider, id)
92
126
  );
93
127
  CREATE INDEX IF NOT EXISTS chat_flow_choice_visit ON chat_flow_choice(card, entry);
128
+ CREATE INDEX IF NOT EXISTS chat_flow_choice_part ON chat_flow_choice(provider, part);
129
+ -- "Use this as your note?" Yes / No about the person's message held (its event; words kept only until answered).
94
130
  CREATE TABLE IF NOT EXISTS chat_flow_note (
95
- token TEXT PRIMARY KEY, part INTEGER NOT NULL REFERENCES chat_part(id),
131
+ ${PROVIDER}, token TEXT NOT NULL, part INTEGER NOT NULL,
96
132
  card INTEGER NOT NULL REFERENCES flow_card(id), entry INTEGER NOT NULL,
97
- held TEXT NOT NULL, words TEXT, answer TEXT NOT NULL CHECK(answer IN ('yes','no')), expires TEXT NOT NULL, consumed TEXT
133
+ held TEXT NOT NULL, words TEXT, answer TEXT NOT NULL CHECK (answer IN ('yes','no')), expires TEXT NOT NULL, consumed TEXT,
134
+ PRIMARY KEY (provider, token),
135
+ FOREIGN KEY (provider, part) REFERENCES chat_part(provider, id)
98
136
  );
99
137
  CREATE INDEX IF NOT EXISTS chat_flow_note_visit ON chat_flow_note(card, entry);
100
- `;
101
- /** v93: a teammate's question in the chat app. One button per option (choice)
102
- * and one to answer in words (choice NULL); that one opens a prompt the
103
- * person's next message in their DM answers. Never in CHAT_TABLES: an older
104
- * database has no such tables until this schema creates them. */
105
- const CHAT_QUESTION_SCHEMA = `
138
+ -- A teammate's question: one button per option and one to answer in words (choice NULL).
106
139
  CREATE TABLE IF NOT EXISTS chat_question_action (
107
- token TEXT PRIMARY KEY, part INTEGER NOT NULL REFERENCES chat_part(id),
108
- question INTEGER NOT NULL REFERENCES teammate_question(id), choice TEXT, expires TEXT NOT NULL, consumed TEXT
140
+ ${PROVIDER}, token TEXT NOT NULL, part INTEGER, binding INTEGER, chat TEXT, message TEXT,
141
+ question INTEGER NOT NULL REFERENCES teammate_question(id), choice TEXT, created TEXT, expires TEXT NOT NULL, consumed TEXT,
142
+ PRIMARY KEY (provider, token),
143
+ FOREIGN KEY (provider, part) REFERENCES chat_part(provider, id)
109
144
  );
110
145
  CREATE INDEX IF NOT EXISTS chat_question_action_question ON chat_question_action(question);
111
146
  CREATE TABLE IF NOT EXISTS chat_question_prompt (
112
- id INTEGER PRIMARY KEY AUTOINCREMENT, binding INTEGER NOT NULL REFERENCES chat_binding(id),
113
- question INTEGER NOT NULL REFERENCES teammate_question(id), created TEXT NOT NULL, expires TEXT NOT NULL, consumed TEXT
147
+ ${PROVIDER}, id INTEGER NOT NULL, binding INTEGER NOT NULL, chat TEXT, message TEXT,
148
+ question INTEGER NOT NULL REFERENCES teammate_question(id), created TEXT NOT NULL, expires TEXT NOT NULL, consumed TEXT,
149
+ PRIMARY KEY (provider, id)
114
150
  );
115
- CREATE INDEX IF NOT EXISTS chat_question_prompt_open ON chat_question_prompt(binding, consumed);
116
- `;
117
- /** The lead's question to its owner (ask_owner) in the chat app: one button per option (choice is its index) and one for
118
- * "Something else" (choice NULL). A tap sends the option as the owner's next message. Never in CHAT_TABLES. */
119
- const CHAT_ASK_SCHEMA = `
151
+ CREATE INDEX IF NOT EXISTS chat_question_prompt_open ON chat_question_prompt(provider, binding, consumed);
152
+ CREATE UNIQUE INDEX IF NOT EXISTS chat_question_prompt_message ON chat_question_prompt(provider, chat, message) WHERE message IS NOT NULL;
153
+ -- The lead's question to its owner (ask_owner): one button per option and one for "Something else" (choice NULL).
120
154
  CREATE TABLE IF NOT EXISTS chat_ask_action (
121
- token TEXT PRIMARY KEY, part INTEGER NOT NULL REFERENCES chat_part(id) ON DELETE CASCADE,
122
- turn INTEGER NOT NULL, choice INTEGER, expires TEXT NOT NULL, consumed TEXT
155
+ ${PROVIDER}, token TEXT NOT NULL, part INTEGER NOT NULL, turn INTEGER NOT NULL, choice INTEGER, expires TEXT NOT NULL, consumed TEXT,
156
+ PRIMARY KEY (provider, token),
157
+ FOREIGN KEY (provider, part) REFERENCES chat_part(provider, id) ON DELETE CASCADE
123
158
  );
124
159
  CREATE INDEX IF NOT EXISTS chat_ask_action_turn ON chat_ask_action(turn);
160
+ -- What one sent message carried, so a reply to it is about exactly that: a notification (kind 'notification', one row
161
+ -- per notification it showed), a builder decision ('decision') or the task a status showed ('task').
162
+ CREATE TABLE IF NOT EXISTS chat_message_ref (
163
+ ${PROVIDER}, binding INTEGER NOT NULL, chat TEXT NOT NULL, message TEXT NOT NULL,
164
+ kind TEXT NOT NULL CHECK (kind IN ('notification','decision','task')),
165
+ notification INTEGER REFERENCES notification(id), destination TEXT, project TEXT,
166
+ task_ref INTEGER REFERENCES task_ref(id), task_id TEXT, run INTEGER,
167
+ decision INTEGER REFERENCES decision(id) ON DELETE CASCADE, created TEXT NOT NULL,
168
+ CHECK ((kind = 'notification') = (notification IS NOT NULL AND destination IS NOT NULL)),
169
+ CHECK ((kind = 'decision') = (decision IS NOT NULL)),
170
+ CHECK (kind <> 'task' OR task_id IS NOT NULL)
171
+ );
172
+ CREATE UNIQUE INDEX IF NOT EXISTS chat_message_ref_notification ON chat_message_ref(provider, binding, chat, message, notification) WHERE kind = 'notification';
173
+ CREATE UNIQUE INDEX IF NOT EXISTS chat_message_ref_one ON chat_message_ref(provider, binding, chat, message, kind) WHERE kind <> 'notification';
174
+ CREATE INDEX IF NOT EXISTS chat_message_ref_destination ON chat_message_ref(destination, notification);
175
+ CREATE INDEX IF NOT EXISTS chat_message_ref_task ON chat_message_ref(destination, task_ref) WHERE kind = 'notification';
176
+ -- A note typed as a reply to a builder decision, held until a tap answers with it. Only a later event replaces it.
177
+ CREATE TABLE IF NOT EXISTS chat_note_draft (
178
+ ${PROVIDER}, id INTEGER NOT NULL, binding INTEGER NOT NULL,
179
+ decision INTEGER NOT NULL REFERENCES decision(id) ON DELETE CASCADE, event INTEGER NOT NULL,
180
+ message TEXT NOT NULL, reply_to TEXT NOT NULL, note TEXT NOT NULL,
181
+ state TEXT NOT NULL DEFAULT 'pending' CHECK (state IN ('pending','armed','superseded','consumed','discarded')),
182
+ created TEXT NOT NULL, expires TEXT NOT NULL,
183
+ PRIMARY KEY (provider, id),
184
+ FOREIGN KEY (provider, binding) REFERENCES chat_binding(provider, id)
185
+ );
186
+ CREATE UNIQUE INDEX IF NOT EXISTS chat_note_draft_live ON chat_note_draft(provider, binding, decision) WHERE state IN ('pending','armed');
187
+ -- Away mode: routine facts wait and go as one digest every every_ms (null: each fact as it lands).
188
+ CREATE TABLE IF NOT EXISTS chat_digest (
189
+ ${PROVIDER}, every_ms INTEGER, set_by TEXT, set_at TEXT, last_sent_at TEXT,
190
+ PRIMARY KEY (provider)
191
+ );
125
192
  `;
126
- const CHAT_TABLES = [
193
+ /** Every shared chat table, parents before children. */
194
+ export const CHAT_TABLES = [
127
195
  "chat_binding",
128
196
  "chat_pair",
129
197
  "chat_event",
@@ -131,6 +199,16 @@ const CHAT_TABLES = [
131
199
  "chat_action",
132
200
  "chat_progress",
133
201
  "chat_runtime",
202
+ "chat_room",
203
+ "chat_meta",
204
+ "chat_flow_action",
205
+ "chat_flow_prompt",
206
+ "chat_flow_choice",
207
+ "chat_flow_note",
208
+ "chat_question_action",
209
+ "chat_question_prompt",
210
+ "chat_ask_action",
211
+ "chat_message_ref",
212
+ "chat_note_draft",
213
+ "chat_digest",
134
214
  ];
135
- export const chatSchema = (channel) => (CHAT_SCHEMA + CHAT_ROOM_SCHEMA + CHAT_FLOW_SCHEMA + CHAT_FLOW_CHOICE_SCHEMA + CHAT_QUESTION_SCHEMA + CHAT_ASK_SCHEMA).replaceAll("chat_", `${channel}_`);
136
- export const chatTables = (channel) => CHAT_TABLES.map((name) => name.replace("chat_", `${channel}_`));
@@ -563,7 +563,6 @@ export declare const COMMAND_OUTPUTS: {
563
563
  approval: z.ZodObject<{
564
564
  approved: z.ZodBoolean;
565
565
  }, z.core.$loose>;
566
- risk: z.ZodString;
567
566
  route: z.ZodUnknown;
568
567
  runs: z.ZodArray<z.ZodUnknown>;
569
568
  deliverable: z.ZodString;
@@ -684,18 +683,6 @@ export declare const COMMAND_OUTPUTS: {
684
683
  reason: z.ZodString;
685
684
  message: z.ZodOptional<z.ZodString>;
686
685
  }, z.core.$loose>], "ok">;
687
- readonly "routine list": z.ZodDiscriminatedUnion<[z.ZodObject<{
688
- envelopeVersion: z.ZodLiteral<1>;
689
- ok: z.ZodLiteral<true>;
690
- command: z.ZodLiteral<"routine list">;
691
- routines: z.ZodArray<z.ZodUnknown>;
692
- }, z.core.$loose>, z.ZodObject<{
693
- envelopeVersion: z.ZodLiteral<1>;
694
- ok: z.ZodLiteral<false>;
695
- command: z.ZodLiteral<"routine list">;
696
- reason: z.ZodString;
697
- message: z.ZodOptional<z.ZodString>;
698
- }, z.core.$loose>], "ok">;
699
686
  readonly "incident list": z.ZodDiscriminatedUnion<[z.ZodObject<{
700
687
  envelopeVersion: z.ZodLiteral<1>;
701
688
  ok: z.ZodLiteral<true>;
@@ -140,7 +140,6 @@ const taskShowOutput = answers("task show", {
140
140
  position: z.unknown(),
141
141
  reservedFor: nullable(str),
142
142
  approval: z.looseObject({ approved: z.boolean() }),
143
- risk: str,
144
143
  route: z.unknown(),
145
144
  runs: list,
146
145
  deliverable: str,
@@ -188,7 +187,6 @@ export const COMMAND_OUTPUTS = {
188
187
  "flows show": answers("flows show", { flow: z.looseObject({ id: int, name: str, repo: str, project: str, readable: z.boolean(), zones: list, triggers: list }) }),
189
188
  grants: answers("grants", { count: int, grants: list }),
190
189
  "runner list": answers("runner list", { count: int, runners: list }),
191
- "routine list": answers("routine list", { routines: list }),
192
190
  "incident list": answers("incident list", { incidents: list }),
193
191
  "outbox list": answers("outbox list", { notifications: rows({ id: int, kind: str, subject: str, createdAt: str }) }),
194
192
  // A pass with failed deliveries answers ok: false with its counts and no reason.