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.
Files changed (175) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/dist/assignment-adapters.d.ts +12 -48
  3. package/dist/assignment-adapters.js +33 -43
  4. package/dist/browser/THIRD_PARTY_NOTICES.txt +3 -3
  5. package/dist/browser/workspace.css +1 -1
  6. package/dist/browser/workspace.js +26 -26
  7. package/dist/browser-crew.js +5 -0
  8. package/dist/browser-workspace.d.ts +2 -0
  9. package/dist/build-review.d.ts +1 -1
  10. package/dist/build-review.js +2 -1
  11. package/dist/chat-acceptance.d.ts +1 -1
  12. package/dist/chat-actions.d.ts +18 -12
  13. package/dist/chat-actions.js +15 -65
  14. package/dist/chat-ask.js +5 -5
  15. package/dist/chat-channel.js +20 -9
  16. package/dist/chat-decide.d.ts +18 -4
  17. package/dist/chat-decide.js +31 -11
  18. package/dist/chat-delivery-state.d.ts +12 -70
  19. package/dist/chat-delivery-state.js +17 -135
  20. package/dist/chat-delivery.js +58 -30
  21. package/dist/chat-flow.js +8 -6
  22. package/dist/chat-inbox.js +1 -1
  23. package/dist/coding-context.d.ts +2 -39
  24. package/dist/coding-context.js +2 -1
  25. package/dist/coding-handoff.d.ts +6 -25
  26. package/dist/coding-handoff.js +11 -5
  27. package/dist/coding-types.d.ts +2 -26
  28. package/dist/coding-workspace.d.ts +2 -0
  29. package/dist/coding-workspace.js +50 -14
  30. package/dist/contracts/acceptance-terms.d.ts +14 -0
  31. package/dist/contracts/acceptance-terms.js +16 -0
  32. package/dist/contracts/chat-actions.d.ts +328 -0
  33. package/dist/contracts/chat-actions.js +133 -0
  34. package/dist/contracts/chat-callback-rows.d.ts +59 -0
  35. package/dist/contracts/chat-callback-rows.js +46 -0
  36. package/dist/contracts/chat-content.d.ts +276 -0
  37. package/dist/contracts/chat-content.js +127 -0
  38. package/dist/contracts/chat-tables.d.ts +6 -0
  39. package/dist/contracts/chat-tables.js +136 -0
  40. package/dist/contracts/coding-context.d.ts +97 -0
  41. package/dist/contracts/coding-context.js +60 -0
  42. package/dist/contracts/coding-handoff.d.ts +44 -0
  43. package/dist/contracts/coding-handoff.js +47 -0
  44. package/dist/contracts/coding-workspace.d.ts +165 -0
  45. package/dist/contracts/coding-workspace.js +71 -0
  46. package/dist/contracts/contract.d.ts +7 -2
  47. package/dist/contracts/contract.js +21 -12
  48. package/dist/contracts/decision.d.ts +45 -0
  49. package/dist/contracts/decision.js +45 -0
  50. package/dist/contracts/discord-callback.d.ts +45 -0
  51. package/dist/contracts/discord-callback.js +34 -0
  52. package/dist/contracts/flow-card.d.ts +50 -0
  53. package/dist/contracts/flow-card.js +46 -0
  54. package/dist/contracts/flow-send.d.ts +119 -0
  55. package/dist/contracts/flow-send.js +112 -0
  56. package/dist/contracts/flow.d.ts +5 -0
  57. package/dist/contracts/flow.js +5 -0
  58. package/dist/contracts/gateway-tools.d.ts +374 -0
  59. package/dist/contracts/gateway-tools.js +94 -0
  60. package/dist/contracts/lead-context.d.ts +165 -0
  61. package/dist/contracts/lead-context.js +73 -0
  62. package/dist/contracts/lead-tools.d.ts +1905 -0
  63. package/dist/contracts/lead-tools.js +265 -0
  64. package/dist/contracts/memory-pass.d.ts +158 -0
  65. package/dist/contracts/memory-pass.js +102 -0
  66. package/dist/contracts/project-knowledge.d.ts +126 -0
  67. package/dist/contracts/project-knowledge.js +93 -0
  68. package/dist/contracts/project-memory.d.ts +117 -0
  69. package/dist/contracts/project-memory.js +51 -0
  70. package/dist/contracts/project-skills.d.ts +121 -0
  71. package/dist/contracts/project-skills.js +76 -0
  72. package/dist/contracts/review-findings.d.ts +46 -0
  73. package/dist/contracts/review-findings.js +48 -0
  74. package/dist/contracts/route.d.ts +484 -0
  75. package/dist/contracts/route.js +128 -0
  76. package/dist/contracts/scope.d.ts +105 -0
  77. package/dist/contracts/scope.js +114 -0
  78. package/dist/contracts/scout-report.d.ts +234 -0
  79. package/dist/contracts/scout-report.js +141 -0
  80. package/dist/contracts/slack-callback.d.ts +43 -0
  81. package/dist/contracts/slack-callback.js +35 -0
  82. package/dist/contracts/sort-answer.d.ts +83 -0
  83. package/dist/contracts/sort-answer.js +77 -0
  84. package/dist/contracts/stage-output.d.ts +127 -0
  85. package/dist/contracts/stage-output.js +166 -0
  86. package/dist/contracts/task-sizing.d.ts +24 -0
  87. package/dist/contracts/task-sizing.js +28 -0
  88. package/dist/contracts/teammate-turn.d.ts +43 -0
  89. package/dist/contracts/teammate-turn.js +51 -0
  90. package/dist/contracts/teams-callback.d.ts +14 -0
  91. package/dist/contracts/teams-callback.js +14 -0
  92. package/dist/contracts/telegram-callback.d.ts +120 -0
  93. package/dist/contracts/telegram-callback.js +97 -0
  94. package/dist/decision.d.ts +11 -20
  95. package/dist/decision.js +86 -167
  96. package/dist/desktop-update.d.ts +1 -1
  97. package/dist/discord-chat.js +31 -20
  98. package/dist/flow-engine.js +14 -17
  99. package/dist/flow-items.d.ts +4 -13
  100. package/dist/flow-items.js +1 -15
  101. package/dist/flow-send.d.ts +6 -37
  102. package/dist/flow-send.js +18 -14
  103. package/dist/flow-share.js +7 -1
  104. package/dist/flow-sort.d.ts +7 -26
  105. package/dist/flow-sort.js +25 -20
  106. package/dist/flow-steps.js +3 -2
  107. package/dist/flows.d.ts +2 -4
  108. package/dist/flows.js +9 -5
  109. package/dist/invoke.js +4 -4
  110. package/dist/lead-context.d.ts +4 -2
  111. package/dist/lead-context.js +24 -17
  112. package/dist/mate-progress.d.ts +26 -0
  113. package/dist/mate-progress.js +24 -0
  114. package/dist/mate-tools.d.ts +4 -0
  115. package/dist/mate-tools.js +108 -275
  116. package/dist/mate.js +11 -2
  117. package/dist/mcp-connect.d.ts +24 -4
  118. package/dist/mcp-connect.js +78 -15
  119. package/dist/mcp.js +84 -216
  120. package/dist/memory-pass.d.ts +17 -42
  121. package/dist/memory-pass.js +54 -26
  122. package/dist/phase-routing.d.ts +19 -72
  123. package/dist/phase-routing.js +34 -164
  124. package/dist/project-knowledge.d.ts +16 -35
  125. package/dist/project-knowledge.js +42 -21
  126. package/dist/project-memory.d.ts +2 -28
  127. package/dist/project-memory.js +28 -13
  128. package/dist/project-skills.d.ts +16 -38
  129. package/dist/project-skills.js +41 -32
  130. package/dist/project-tools.d.ts +1 -0
  131. package/dist/project-tools.js +4 -4
  132. package/dist/provider.d.ts +2 -0
  133. package/dist/provider.js +4 -16
  134. package/dist/result-shots.d.ts +1 -1
  135. package/dist/result-shots.js +2 -1
  136. package/dist/review-switch.d.ts +3 -8
  137. package/dist/reviewer.d.ts +8 -12
  138. package/dist/reviewer.js +11 -35
  139. package/dist/scope.d.ts +21 -43
  140. package/dist/scope.js +42 -128
  141. package/dist/scout-report.d.ts +9 -194
  142. package/dist/scout-report.js +113 -264
  143. package/dist/scout.js +15 -11
  144. package/dist/serve.js +23 -5
  145. package/dist/setup-guide.d.ts +2 -2
  146. package/dist/slack-chat.js +26 -20
  147. package/dist/slack-state.js +2 -1
  148. package/dist/store.d.ts +9 -32
  149. package/dist/store.js +18 -15
  150. package/dist/structured-output.js +2 -1
  151. package/dist/subscription-chat.d.ts +4 -0
  152. package/dist/subscription-chat.js +4 -1
  153. package/dist/task-review-brief.d.ts +1 -7
  154. package/dist/task-review-brief.js +6 -6
  155. package/dist/task-sizing.d.ts +3 -27
  156. package/dist/task-sizing.js +11 -26
  157. package/dist/task-text.d.ts +0 -5
  158. package/dist/task-text.js +1 -3
  159. package/dist/teammate-question.js +7 -5
  160. package/dist/teammate-work.js +9 -5
  161. package/dist/teammates.d.ts +13 -64
  162. package/dist/teammates.js +32 -33
  163. package/dist/teams-chat.js +20 -11
  164. package/dist/telegram-flow.d.ts +9 -0
  165. package/dist/telegram-flow.js +44 -8
  166. package/dist/telegram-mate.d.ts +4 -14
  167. package/dist/telegram-mate.js +9 -8
  168. package/dist/telegram-team.d.ts +2 -19
  169. package/dist/telegram.d.ts +1 -1
  170. package/dist/telegram.js +94 -68
  171. package/dist/text-limits.d.ts +142 -1
  172. package/dist/text-limits.js +144 -1
  173. package/dist/tools-ui.d.ts +2 -1
  174. package/dist/tools-ui.js +7 -4
  175. package/package.json +17 -12
@@ -0,0 +1,35 @@
1
+ /**
2
+ * A Slack button tap (slack-chat.ts `receiveSlack`): the `block_actions` interaction Slack sends, and the button data
3
+ * Toolroll put on it. The interaction is Slack's, so fields Toolroll doesn't read are ignored; the button (its
4
+ * `action_id` and one-time token `value`) is Toolroll's. A tap from the paired person whose button can't be read is
5
+ * answered with why (`actions[0].value: must be a Toolroll button token`), and nothing is done.
6
+ */
7
+ import { z } from "zod";
8
+ import { parseContract } from "./contract.js";
9
+ /** Toolroll's buttons: toolroll_* since the rename; standing_orders_* buttons on older messages still work. */
10
+ export const SLACK_BUTTON_ACTION = /^(?:toolroll|standing_orders)_(?:confirm|dismiss|yes|cancel|flow_approve|flow_edit|flow_send_back|flow_choose_[0-3]|flow_note_(?:yes|no)|question_choice|question_words)$/;
11
+ /** A link button opens its page in Slack's own client; Slack still reports the tap, and there is nothing to do. */
12
+ export const SLACK_LINK_ACTION = /^(?:toolroll|standing_orders)_link(?:_[0-9]+)?$/;
13
+ const token = z.string().regex(/^[a-f0-9]{32}$/, { error: "must be a Toolroll button token" });
14
+ /** The interaction around a tap: who, and the message the button is on. */
15
+ export const slackBlockActionsSchema = z.object({
16
+ type: z.literal("block_actions"),
17
+ user: z.object({ id: z.string() }),
18
+ container: z.object({ type: z.string(), channel_id: z.string().optional(), message_ts: z.string().optional() }),
19
+ message: z.object({ thread_ts: z.string().optional() }).optional(),
20
+ actions: z.array(z.object({ action_id: z.string().optional(), value: z.string().optional(), action_ts: z.string().optional() })),
21
+ });
22
+ /** The one tapped button, as Toolroll made it. */
23
+ export const slackButtonSchema = z.object({
24
+ action_id: z.string().regex(SLACK_BUTTON_ACTION, { error: "not a Toolroll button" }),
25
+ value: token,
26
+ action_ts: z.string().regex(/^\d{10,16}\.\d{6}$/, { error: "must be a Slack timestamp" }),
27
+ });
28
+ export function readSlackBlockActions(body) {
29
+ return parseContract(slackBlockActionsSchema, body);
30
+ }
31
+ /** The tapped button: exactly one, Toolroll's (`actions[0].action_id: not a Toolroll button`). */
32
+ export function readSlackButton(interaction) {
33
+ const read = parseContract(z.object({ actions: z.array(slackButtonSchema).length(1) }), { actions: interaction.actions });
34
+ return read.ok ? { ok: true, value: read.value.actions[0] } : read;
35
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * A Sort zone's answer (flow-sort.ts): what Jev, through OpenRouter's Decisions API, says about a card, and what a
3
+ * sort step keeps of it. The Decisions API takes questions, not a JSON Schema, so there is no model-facing schema:
4
+ * the request's keys come from the flow contract (`SORT_ROUTE_KEY`, `sortNoteKeyOf`, `sortKeyOf` in flow.ts), and
5
+ * `jevReplySchema` is how the reply is read before anything routes on it.
6
+ *
7
+ * The reply is an outside service's: keys Toolroll does not read (ids, token counts) are ignored rather than refused.
8
+ * Optional metadata can have any type, as before: the reader defaults non-numeric confidences/chances to zero,
9
+ * skips non-numeric notes, and defaults the model and cost. The route choice must still be text.
10
+ *
11
+ * `sortDecisionSchema` is the decision a sort step keeps (`flow_step_run.decision_json`), unversioned as every release
12
+ * has written it; the card, Insights and spend read saved ones as before.
13
+ */
14
+ import { z } from "zod";
15
+ import { type ContractResult } from "./contract.js";
16
+ /** Jev's answer to the zone's question: the key of the answer it picked, how sure it is (0–1), and every answer's chance. */
17
+ export declare const jevChoiceSchema: z.ZodObject<{
18
+ choice: z.ZodString;
19
+ confidence: z.ZodOptional<z.ZodUnknown>;
20
+ probabilities: z.ZodOptional<z.ZodUnknown>;
21
+ }, z.core.$strip>;
22
+ /** Jev's answer to something else the zone notes: a score's level (as a number) or a yes/no's chance of yes. */
23
+ export declare const jevNoteSchema: z.ZodObject<{
24
+ score: z.ZodOptional<z.ZodUnknown>;
25
+ noul: z.ZodOptional<z.ZodUnknown>;
26
+ confidence: z.ZodOptional<z.ZodUnknown>;
27
+ }, z.core.$strip>;
28
+ /** What Toolroll reads of Jev's reply. */
29
+ export declare const jevReplySchema: z.ZodObject<{
30
+ model: z.ZodOptional<z.ZodUnknown>;
31
+ answers: z.ZodObject<{
32
+ route: z.ZodObject<{
33
+ choice: z.ZodString;
34
+ confidence: z.ZodOptional<z.ZodUnknown>;
35
+ probabilities: z.ZodOptional<z.ZodUnknown>;
36
+ }, z.core.$strip>;
37
+ }, z.core.$catchall<z.ZodObject<{
38
+ score: z.ZodOptional<z.ZodUnknown>;
39
+ noul: z.ZodOptional<z.ZodUnknown>;
40
+ confidence: z.ZodOptional<z.ZodUnknown>;
41
+ }, z.core.$strip>>>;
42
+ usage: z.ZodOptional<z.ZodUnknown>;
43
+ }, z.core.$strip>;
44
+ export type JevReply = z.infer<typeof jevReplySchema>;
45
+ /** Read Jev's reply, or path-named lines saying what it lacks (`answers.route.choice: required`). */
46
+ export declare function readJevReply(input: unknown): ContractResult<JevReply>;
47
+ /** One thing the zone also noted, as the card shows it: the level or yes/no, and how sure (0–1). */
48
+ export declare const sortNoteAnswerSchema: z.ZodObject<{
49
+ id: z.ZodString;
50
+ question: z.ZodString;
51
+ kind: z.ZodEnum<{
52
+ score: "score";
53
+ "yes-no": "yes-no";
54
+ }>;
55
+ answer: z.ZodString;
56
+ sure: z.ZodNumber;
57
+ }, z.core.$strict>;
58
+ export type SortNoteAnswer = z.infer<typeof sortNoteAnswerSchema>;
59
+ /** What a sort step decided, kept on its run (decision_json) and shown on the card. */
60
+ export declare const sortDecisionSchema: z.ZodObject<{
61
+ model: z.ZodString;
62
+ answer: z.ZodString;
63
+ sure: z.ZodNumber;
64
+ sureAt: z.ZodNumber;
65
+ confident: z.ZodBoolean;
66
+ to: z.ZodNullable<z.ZodString>;
67
+ chances: z.ZodObject<{}, z.core.$catchall<z.ZodNumber>>;
68
+ notes: z.ZodArray<z.ZodObject<{
69
+ id: z.ZodString;
70
+ question: z.ZodString;
71
+ kind: z.ZodEnum<{
72
+ score: "score";
73
+ "yes-no": "yes-no";
74
+ }>;
75
+ answer: z.ZodString;
76
+ sure: z.ZodNumber;
77
+ }, z.core.$strict>>;
78
+ cost: z.ZodNullable<z.ZodNumber>;
79
+ ms: z.ZodNumber;
80
+ }, z.core.$strict>;
81
+ export type SortDecision = z.infer<typeof sortDecisionSchema>;
82
+ /** Read a sort decision as a step keeps it. */
83
+ export declare function readSortDecision(input: unknown): ContractResult<SortDecision>;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * A Sort zone's answer (flow-sort.ts): what Jev, through OpenRouter's Decisions API, says about a card, and what a
3
+ * sort step keeps of it. The Decisions API takes questions, not a JSON Schema, so there is no model-facing schema:
4
+ * the request's keys come from the flow contract (`SORT_ROUTE_KEY`, `sortNoteKeyOf`, `sortKeyOf` in flow.ts), and
5
+ * `jevReplySchema` is how the reply is read before anything routes on it.
6
+ *
7
+ * The reply is an outside service's: keys Toolroll does not read (ids, token counts) are ignored rather than refused.
8
+ * Optional metadata can have any type, as before: the reader defaults non-numeric confidences/chances to zero,
9
+ * skips non-numeric notes, and defaults the model and cost. The route choice must still be text.
10
+ *
11
+ * `sortDecisionSchema` is the decision a sort step keeps (`flow_step_run.decision_json`), unversioned as every release
12
+ * has written it; the card, Insights and spend read saved ones as before.
13
+ */
14
+ import { z } from "zod";
15
+ import { parseContract } from "./contract.js";
16
+ import { flowSortAnswerSchema, flowSortNoteSchema, SORT_ROUTE_KEY } from "./flow.js";
17
+ /** Jev's answer to the zone's question: the key of the answer it picked, how sure it is (0–1), and every answer's chance. */
18
+ export const jevChoiceSchema = z.object({
19
+ choice: z.string(),
20
+ confidence: z.unknown().optional(),
21
+ probabilities: z.unknown().optional(),
22
+ });
23
+ /** Jev's answer to something else the zone notes: a score's level (as a number) or a yes/no's chance of yes. */
24
+ export const jevNoteSchema = z.object({
25
+ score: z.unknown().optional(),
26
+ noul: z.unknown().optional(),
27
+ confidence: z.unknown().optional(),
28
+ });
29
+ /** What Toolroll reads of Jev's reply. */
30
+ export const jevReplySchema = z.object({
31
+ model: z.unknown().optional(),
32
+ answers: z.object({ [SORT_ROUTE_KEY]: jevChoiceSchema }).catchall(jevNoteSchema),
33
+ usage: z.unknown().optional(),
34
+ });
35
+ /** Read Jev's reply, or path-named lines saying what it lacks (`answers.route.choice: required`). */
36
+ export function readJevReply(input) {
37
+ if (typeof input === "object" && input !== null && !Array.isArray(input)) {
38
+ const body = input;
39
+ const answers = body["answers"];
40
+ if (typeof answers === "object" && answers !== null && !Array.isArray(answers)) {
41
+ // Extra answers are not necessarily notes. Non-objects have no note fields to read, so ignore them.
42
+ input = { ...body, answers: Object.fromEntries(Object.entries(answers).map(([key, value]) => [key,
43
+ key === SORT_ROUTE_KEY || (typeof value === "object" && value !== null && !Array.isArray(value)) ? value : {},
44
+ ])) };
45
+ }
46
+ }
47
+ return parseContract(jevReplySchema, input);
48
+ }
49
+ const noteShape = flowSortNoteSchema.options[0].shape;
50
+ /** One thing the zone also noted, as the card shows it: the level or yes/no, and how sure (0–1). */
51
+ export const sortNoteAnswerSchema = z.strictObject({
52
+ id: noteShape.id,
53
+ question: noteShape.question.describe("The question the zone also notes."),
54
+ kind: z.enum(["score", "yes-no"]),
55
+ answer: z.string(),
56
+ sure: z.number().min(0).max(1),
57
+ });
58
+ /** What a sort step decided, kept on its run (decision_json) and shown on the card. */
59
+ export const sortDecisionSchema = z.strictObject({
60
+ model: z.string(),
61
+ /** The answer as the zone names it, and how sure Jev was (0–1). */
62
+ answer: flowSortAnswerSchema.shape.answer.describe("The zone's chosen answer."),
63
+ sure: z.number().min(0).max(1),
64
+ sureAt: z.number(),
65
+ /** Sure enough to act alone, and where the card went (null: it waited in the zone). */
66
+ confident: z.boolean(),
67
+ to: flowSortAnswerSchema.shape.to.nullable(),
68
+ /** Every answer's chance, by name. */
69
+ chances: z.object({}).catchall(z.number()),
70
+ notes: z.array(sortNoteAnswerSchema),
71
+ cost: z.number().nullable(),
72
+ ms: z.number(),
73
+ });
74
+ /** Read a sort decision as a step keeps it. */
75
+ export function readSortDecision(input) {
76
+ return parseContract(sortDecisionSchema, input);
77
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * What a finished zone hands the zones after it (docs/plans/zod-revamp.md, item 8): its `{{stage.<id>}}` words and, by
3
+ * kind, more fields (a research zone's `{{stage.<id>.items}}` and `{{stage.<id>.report}}`), kept on the card as one
4
+ * versioned payload (`flow_card.outputs_json`) with any output too long to pass on attached whole.
5
+ *
6
+ * - `stageHandoffSchema` is one zone's handoff; `STAGE_HANDOFFS` says which of its fields each zone kind hands on. A
7
+ * template's `{{stage.<id>.<field>}}` is checked against it when a flow is saved (`stageReferenceProblems`), so a
8
+ * refusal names the path: `stages[1].instructions: stage.research.unknown is not available`.
9
+ * - `cardOutputsSchema` is what the card keeps: every handoff's fields flat (`<zone>`, `<zone>.items`, `<zone>.report`,
10
+ * as `{{stage.…}}` names them) and, for a zone whose output was over TEXT_LIMITS.stageOutput, the output whole: its
11
+ * `{{stage.<zone>}}` says where it is kept, and a task filed after it is given it whole (flowGoalCuts).
12
+ *
13
+ * Saved before this contract (version 0), a card's outputs were the flat map alone; they read exactly as every release
14
+ * read them: the string values, anything else skipped.
15
+ */
16
+ import { z } from "zod";
17
+ import { type ContractIssue, type ContractResult } from "./contract.js";
18
+ import type { FlowDefinition, FlowStage, FlowStageKind } from "./flow.js";
19
+ /** One zone's handoff to the zones after it. */
20
+ export declare const stageHandoffSchema: z.ZodObject<{
21
+ text: z.ZodString;
22
+ items: z.ZodOptional<z.ZodString>;
23
+ report: z.ZodOptional<z.ZodString>;
24
+ }, z.core.$strict>;
25
+ export type StageHandoff = z.infer<typeof stageHandoffSchema>;
26
+ export type StageField = Exclude<keyof StageHandoff, "text">;
27
+ /**
28
+ * What each zone kind hands on, or null when it hands nothing on (it holds, asks, messages or ends). A sort hands on its
29
+ * decision in words (`sortWords` of the small-answers contract's SortDecision, src/contracts/sort-answer.ts), and a
30
+ * Send to me zone what it sent.
31
+ */
32
+ export declare const STAGE_HANDOFFS: {
33
+ readonly inbox: null;
34
+ readonly task: z.ZodObject<{
35
+ text: z.ZodString;
36
+ }, z.core.$strict>;
37
+ readonly report: z.ZodObject<{
38
+ text: z.ZodString;
39
+ items: z.ZodOptional<z.ZodString>;
40
+ report: z.ZodOptional<z.ZodString>;
41
+ }, z.core.$strict>;
42
+ readonly approval: null;
43
+ readonly check: z.ZodObject<{
44
+ text: z.ZodString;
45
+ }, z.core.$strict>;
46
+ readonly "pull-request": z.ZodObject<{
47
+ text: z.ZodString;
48
+ }, z.core.$strict>;
49
+ readonly update: z.ZodObject<{
50
+ text: z.ZodString;
51
+ }, z.core.$strict>;
52
+ readonly notify: null;
53
+ readonly sort: z.ZodObject<{
54
+ text: z.ZodString;
55
+ }, z.core.$strict>;
56
+ readonly draft: z.ZodObject<{
57
+ text: z.ZodString;
58
+ }, z.core.$strict>;
59
+ readonly request: z.ZodObject<{
60
+ text: z.ZodString;
61
+ }, z.core.$strict>;
62
+ readonly email: z.ZodObject<{
63
+ text: z.ZodString;
64
+ }, z.core.$strict>;
65
+ readonly tool: z.ZodObject<{
66
+ text: z.ZodString;
67
+ }, z.core.$strict>;
68
+ readonly wait: z.ZodObject<{
69
+ text: z.ZodString;
70
+ }, z.core.$strict>;
71
+ readonly teammate: z.ZodObject<{
72
+ text: z.ZodString;
73
+ }, z.core.$strict>;
74
+ readonly send: z.ZodObject<{
75
+ text: z.ZodString;
76
+ }, z.core.$strict>;
77
+ readonly choose: null;
78
+ readonly done: null;
79
+ };
80
+ /** The extra `{{stage.<id>.<field>}}` fields a zone kind hands on. */
81
+ export declare function stageFieldsOf(kind: FlowStageKind): StageField[];
82
+ /** One zone's handoff, read from the card's flat outputs (a field it never wrote is left out; no words read as ""). */
83
+ export declare function stageHandoffOf(outputs: Readonly<Record<string, string>>, zone: string): StageHandoff;
84
+ /** The card's flat outputs with one zone's handoff in place of its last: a field this visit didn't hand on is gone, so
85
+ * nothing stale is left behind. */
86
+ export declare function withStageHandoff(outputs: Readonly<Record<string, string>>, zone: string, handoff: StageHandoff): Record<string, string>;
87
+ export declare const CARD_OUTPUTS_VERSION = 1;
88
+ export declare const cardOutputsSchema: z.ZodObject<{
89
+ version: z.ZodLiteral<1>;
90
+ outputs: z.ZodObject<{}, z.core.$catchall<z.ZodString>>;
91
+ attached: z.ZodObject<{}, z.core.$catchall<z.ZodString>>;
92
+ }, z.core.$strict>;
93
+ export type CardOutputs = z.infer<typeof cardOutputsSchema>;
94
+ /** Version 0, the flat map every release before wrote: its string values, as they read them; anything else is skipped. */
95
+ export declare function upgradeFlatOutputs(raw: unknown): CardOutputs;
96
+ /**
97
+ * Read a card's outputs. Version 1 as itself and a newer version refused plainly. Anything without a numeric `version`
98
+ * was saved before outputs carried one (a zone may be called `version`; its words were a string), and is read as the
99
+ * flat map it is.
100
+ */
101
+ export declare function readCardOutputs(input: unknown): ContractResult<CardOutputs>;
102
+ /** A card's outputs as the store keeps them: unreadable JSON is the empty card it always was; `newer` is a row a newer
103
+ * Toolroll wrote, which this one reads as empty and never writes over. */
104
+ export declare function cardOutputsFromStore(json: string): {
105
+ outputs: Record<string, string>;
106
+ attached: Record<string, string>;
107
+ issues: ContractIssue[];
108
+ newer: boolean;
109
+ };
110
+ /** A card's outputs as the store saves them. A zone's attachment is kept only while its output is the pointer to it. */
111
+ export declare function cardOutputsForStore(outputs: Readonly<Record<string, string>>, attached: Readonly<Record<string, string>>): string;
112
+ /** Where a zone's words take fill-ins, by the drawing's path: its instructions and message, a request's address,
113
+ * headers and body, an email's fields and a tool's arguments. */
114
+ export declare function templateFields(stage: FlowStage): {
115
+ path: string;
116
+ text: string;
117
+ }[];
118
+ export declare function stageReferences(text: string): string[];
119
+ /** What one `{{stage.…}}` reference can't be, against the drawing: null when its zone hands that field on. */
120
+ export declare function stageReferenceProblem(definition: FlowDefinition, reference: string): string | null;
121
+ /**
122
+ * Template references a save adds that its zones don't hand on, each named by its path
123
+ * (`stages[1].instructions: stage.research.unknown is not available ...`). A reference the flow already had before
124
+ * this save (`previous`, the saved drawing) is never refused, nor is any reference when a flow is only read or run:
125
+ * this checks new and changed words alone.
126
+ */
127
+ export declare function stageReferenceProblems(definition: FlowDefinition, previous: FlowDefinition | null): ContractIssue[];
@@ -0,0 +1,166 @@
1
+ /**
2
+ * What a finished zone hands the zones after it (docs/plans/zod-revamp.md, item 8): its `{{stage.<id>}}` words and, by
3
+ * kind, more fields (a research zone's `{{stage.<id>.items}}` and `{{stage.<id>.report}}`), kept on the card as one
4
+ * versioned payload (`flow_card.outputs_json`) with any output too long to pass on attached whole.
5
+ *
6
+ * - `stageHandoffSchema` is one zone's handoff; `STAGE_HANDOFFS` says which of its fields each zone kind hands on. A
7
+ * template's `{{stage.<id>.<field>}}` is checked against it when a flow is saved (`stageReferenceProblems`), so a
8
+ * refusal names the path: `stages[1].instructions: stage.research.unknown is not available`.
9
+ * - `cardOutputsSchema` is what the card keeps: every handoff's fields flat (`<zone>`, `<zone>.items`, `<zone>.report`,
10
+ * as `{{stage.…}}` names them) and, for a zone whose output was over TEXT_LIMITS.stageOutput, the output whole: its
11
+ * `{{stage.<zone>}}` says where it is kept, and a task filed after it is given it whole (flowGoalCuts).
12
+ *
13
+ * Saved before this contract (version 0), a card's outputs were the flat map alone; they read exactly as every release
14
+ * read them: the string values, anything else skipped.
15
+ */
16
+ import { z } from "zod";
17
+ import { parseContract, readVersioned, versioned } from "./contract.js";
18
+ /** One zone's handoff to the zones after it. */
19
+ export const stageHandoffSchema = z.strictObject({
20
+ text: z.string().describe("{{stage.<id>}}: what the zone produced, whole up to TEXT_LIMITS.stageOutput characters; a longer one says where it is kept whole."),
21
+ items: z.string().optional().describe("{{stage.<id>.items}}: what a research zone found, as a numbered list (title, why, link)."),
22
+ report: z.string().optional().describe("{{stage.<id>.report}}: a research zone's whole report (REPORT_FILL_LIMIT)."),
23
+ });
24
+ const words = stageHandoffSchema.pick({ text: true });
25
+ /**
26
+ * What each zone kind hands on, or null when it hands nothing on (it holds, asks, messages or ends). A sort hands on its
27
+ * decision in words (`sortWords` of the small-answers contract's SortDecision, src/contracts/sort-answer.ts), and a
28
+ * Send to me zone what it sent.
29
+ */
30
+ export const STAGE_HANDOFFS = {
31
+ inbox: null, task: words, report: stageHandoffSchema, approval: null, check: words, "pull-request": words, update: words, notify: null,
32
+ sort: words, draft: words, request: words, email: words, tool: words, wait: words, teammate: words, send: words, choose: null, done: null,
33
+ };
34
+ /** The extra `{{stage.<id>.<field>}}` fields a zone kind hands on. */
35
+ export function stageFieldsOf(kind) {
36
+ const schema = STAGE_HANDOFFS[kind];
37
+ return schema === null ? [] : Object.keys(schema.shape).filter(field => field !== "text");
38
+ }
39
+ /** One zone's handoff, read from the card's flat outputs (a field it never wrote is left out; no words read as ""). */
40
+ export function stageHandoffOf(outputs, zone) {
41
+ const fields = Object.keys(stageHandoffSchema.shape).filter(field => field !== "text");
42
+ return { text: outputs[zone] ?? "", ...Object.fromEntries(fields.flatMap(field => outputs[`${zone}.${field}`] === undefined ? [] : [[field, outputs[`${zone}.${field}`]]])) };
43
+ }
44
+ /** The card's flat outputs with one zone's handoff in place of its last: a field this visit didn't hand on is gone, so
45
+ * nothing stale is left behind. */
46
+ export function withStageHandoff(outputs, zone, handoff) {
47
+ const read = stageHandoffSchema.parse(handoff);
48
+ const next = { ...outputs, [zone]: read.text };
49
+ for (const field of Object.keys(stageHandoffSchema.shape).filter(one => one !== "text")) {
50
+ const value = read[field];
51
+ if (value === undefined)
52
+ delete next[`${zone}.${field}`];
53
+ else
54
+ next[`${zone}.${field}`] = value;
55
+ }
56
+ return next;
57
+ }
58
+ // ------------------------------------------------------------------ what the card keeps
59
+ export const CARD_OUTPUTS_VERSION = 1;
60
+ export const cardOutputsSchema = versioned(CARD_OUTPUTS_VERSION, {
61
+ outputs: z.object({}).catchall(z.string()).describe("Every {{stage.…}} value by its name: <zone>, and a research zone's <zone>.items and <zone>.report."),
62
+ attached: z.object({}).catchall(z.string()).describe("A zone's output in full when it was over TEXT_LIMITS.stageOutput characters, by zone."),
63
+ });
64
+ /** Version 0, the flat map every release before wrote: its string values, as they read them; anything else is skipped. */
65
+ export function upgradeFlatOutputs(raw) {
66
+ const outputs = raw !== null && typeof raw === "object" ? Object.fromEntries(Object.entries(raw).filter((entry) => typeof entry[1] === "string")) : {};
67
+ return { version: CARD_OUTPUTS_VERSION, outputs, attached: {} };
68
+ }
69
+ /**
70
+ * Read a card's outputs. Version 1 as itself and a newer version refused plainly. Anything without a numeric `version`
71
+ * was saved before outputs carried one (a zone may be called `version`; its words were a string), and is read as the
72
+ * flat map it is.
73
+ */
74
+ export function readCardOutputs(input) {
75
+ const legacy = input === null || typeof input !== "object" || Array.isArray(input) || typeof input["version"] !== "number";
76
+ return legacy ? parseContract(cardOutputsSchema, upgradeFlatOutputs(input)) : readVersioned(cardOutputsSchema, input);
77
+ }
78
+ /** A card's outputs as the store keeps them: unreadable JSON is the empty card it always was; `newer` is a row a newer
79
+ * Toolroll wrote, which this one reads as empty and never writes over. */
80
+ export function cardOutputsFromStore(json) {
81
+ let raw;
82
+ try {
83
+ raw = JSON.parse(json);
84
+ }
85
+ catch {
86
+ raw = {};
87
+ }
88
+ const read = readCardOutputs(raw);
89
+ if (read.ok)
90
+ return { outputs: read.value.outputs, attached: read.value.attached, issues: [], newer: false };
91
+ return { outputs: {}, attached: {}, issues: read.issues, newer: read.issues.some(one => one.kind === "newer-version") };
92
+ }
93
+ /** A card's outputs as the store saves them. A zone's attachment is kept only while its output is the pointer to it. */
94
+ export function cardOutputsForStore(outputs, attached) {
95
+ const kept = Object.fromEntries(Object.entries(attached).filter(([zone]) => outputs[zone] !== undefined));
96
+ return JSON.stringify(cardOutputsSchema.parse({ version: CARD_OUTPUTS_VERSION, outputs, attached: kept }));
97
+ }
98
+ // ------------------------------------------------------------------ template references, checked at save
99
+ /** Where a zone's words take fill-ins, by the drawing's path: its instructions and message, a request's address,
100
+ * headers and body, an email's fields and a tool's arguments. */
101
+ export function templateFields(stage) {
102
+ const fields = [];
103
+ const add = (path, text) => { if (typeof text === "string")
104
+ fields.push({ path, text }); };
105
+ add("instructions", stage.instructions);
106
+ add("message", stage.message);
107
+ if (stage.request !== undefined) {
108
+ add("request.url", stage.request.url);
109
+ for (const [name, value] of Object.entries(stage.request.headers))
110
+ add(`request.headers.${name}`, value);
111
+ add("request.body", stage.request.body);
112
+ }
113
+ if (stage.email !== undefined) {
114
+ add("email.to", stage.email.to);
115
+ add("email.subject", stage.email.subject);
116
+ add("email.body", stage.email.body);
117
+ }
118
+ if (stage.tool !== undefined)
119
+ add("tool.args", stage.tool.args);
120
+ return fields;
121
+ }
122
+ /** Every `{{stage.…}}` in some words, as written between the braces (`stage.research.items`). */
123
+ const STAGE_REFERENCE = /\{\{\s*(stage\.[^{}]*?)\s*\}\}/g;
124
+ export function stageReferences(text) {
125
+ return [...text.matchAll(STAGE_REFERENCE)].map(match => match[1]);
126
+ }
127
+ /** What one `{{stage.…}}` reference can't be, against the drawing: null when its zone hands that field on. */
128
+ export function stageReferenceProblem(definition, reference) {
129
+ const [zone = "", ...rest] = reference.slice("stage.".length).split(".");
130
+ const field = rest.join(".");
131
+ const stage = definition.stages.find(one => one.id === zone);
132
+ if (stage === undefined)
133
+ return `${reference} is not available: there's no zone called ${zone === "" ? "(none)" : zone}`;
134
+ if (STAGE_HANDOFFS[stage.kind] === null)
135
+ return `${reference} is not available: ${stage.title} hands nothing on`;
136
+ if (field === "")
137
+ return null;
138
+ const fields = stageFieldsOf(stage.kind);
139
+ if (fields.includes(field))
140
+ return null;
141
+ const offered = [`{{stage.${zone}}}`, ...fields.map(one => `{{stage.${zone}.${one}}}`)];
142
+ return `${reference} is not available (${stage.title} hands on ${offered.length === 1 ? offered[0] : `${offered.slice(0, -1).join(", ")} and ${offered.at(-1)}`})`;
143
+ }
144
+ /**
145
+ * Template references a save adds that its zones don't hand on, each named by its path
146
+ * (`stages[1].instructions: stage.research.unknown is not available ...`). A reference the flow already had before
147
+ * this save (`previous`, the saved drawing) is never refused, nor is any reference when a flow is only read or run:
148
+ * this checks new and changed words alone.
149
+ */
150
+ export function stageReferenceProblems(definition, previous) {
151
+ const had = new Set((previous?.stages ?? []).flatMap(stage => templateFields(stage).flatMap(field => stageReferences(field.text))));
152
+ const problems = [];
153
+ definition.stages.forEach((stage, index) => {
154
+ for (const field of templateFields(stage)) {
155
+ for (const reference of new Set(stageReferences(field.text))) {
156
+ if (had.has(reference))
157
+ continue;
158
+ const problem = stageReferenceProblem(definition, reference);
159
+ const path = `stages[${index}].${field.path}`;
160
+ if (problem !== null)
161
+ problems.push({ path, kind: "bad-value", line: `${path}: ${problem}` });
162
+ }
163
+ }
164
+ });
165
+ return problems;
166
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The task sizing classifier's answer (task-sizing.ts): one schema for the JSON Schema a Claude sizer is given
3
+ * (`--json-schema`) and the reader that takes an answer back. Jev answers through OpenRouter's Decisions API, which
4
+ * takes questions rather than a JSON Schema; its choice and yes/no are read into this same answer before they count.
5
+ *
6
+ * An answer is never kept as it is (it becomes the task's sizing, phase-routing.ts `TaskSizing`), so it carries no
7
+ * version.
8
+ */
9
+ import { z } from "zod";
10
+ import { type ContractResult } from "./contract.js";
11
+ export declare const sizeAnswerSchema: z.ZodObject<{
12
+ size: z.ZodEnum<{
13
+ large: "large";
14
+ medium: "medium";
15
+ small: "small";
16
+ }>;
17
+ risky: z.ZodBoolean;
18
+ reason: z.ZodString;
19
+ }, z.core.$strip>;
20
+ export type SizeAnswer = z.infer<typeof sizeAnswerSchema>;
21
+ /** What a Claude sizer's `--json-schema` is: the answer schema, exactly. */
22
+ export declare const SIZING_MODEL_SCHEMA: Readonly<Record<string, unknown>>;
23
+ /** Keep the old reason defaults and clipping before checking the normalized answer's contract. */
24
+ export declare function readSizeAnswer(value: unknown): ContractResult<SizeAnswer>;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The task sizing classifier's answer (task-sizing.ts): one schema for the JSON Schema a Claude sizer is given
3
+ * (`--json-schema`) and the reader that takes an answer back. Jev answers through OpenRouter's Decisions API, which
4
+ * takes questions rather than a JSON Schema; its choice and yes/no are read into this same answer before they count.
5
+ *
6
+ * An answer is never kept as it is (it becomes the task's sizing, phase-routing.ts `TaskSizing`), so it carries no
7
+ * version.
8
+ */
9
+ import { z } from "zod";
10
+ import { TASK_SIZES } from "../phase-routing.js";
11
+ import { TEXT_LIMITS } from "../text-limits.js";
12
+ import { limited, parseContract, toModelSchema } from "./contract.js";
13
+ export const sizeAnswerSchema = z.object({
14
+ size: z.enum(TASK_SIZES),
15
+ risky: z.boolean(),
16
+ reason: limited("reason", "sizingReason").describe(`reason: at most ${TEXT_LIMITS.sizingReason} characters.`),
17
+ });
18
+ /** What a Claude sizer's `--json-schema` is: the answer schema, exactly. */
19
+ export const SIZING_MODEL_SCHEMA = toModelSchema(sizeAnswerSchema);
20
+ /** Keep the old reason defaults and clipping before checking the normalized answer's contract. */
21
+ export function readSizeAnswer(value) {
22
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
23
+ const body = value;
24
+ const reason = typeof body["reason"] === "string" ? body["reason"].replace(/\s+/g, " ").trim() : "";
25
+ value = { ...body, reason: reason.length <= TEXT_LIMITS.sizingReason ? reason : `${reason.slice(0, TEXT_LIMITS.sizingReason - 1)}…` };
26
+ }
27
+ return parseContract(sizeAnswerSchema, value);
28
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * A teammate's turn (teammates.ts): one schema for the JSON Schema Claude answers in (`--json-schema`) and the reader
3
+ * that takes the answer back. One flat object with every field (a root union is refused by the API); fields a turn
4
+ * does not use are "" (or [] for options).
5
+ *
6
+ * The text limits (TURN_LIMITS, from TEXT_LIMITS) are deliberately not in the schema: the CLI refuses an answer a few
7
+ * characters over one whole, so they are checked after parsing and an answer over one is asked once to shorten
8
+ * (teammate-work.ts), then kept whole. What the zone allows — which actions, which answers, a note for a send-back, a
9
+ * question for an ask, a tool for a tool call — is checked after parsing too (`readTurn`), by path.
10
+ *
11
+ * A turn is kept as the step's decision (`flow_step_run.decision_json`) as it was read, so a saved one is this shape.
12
+ */
13
+ import { z } from "zod";
14
+ import { type ContractResult } from "./contract.js";
15
+ /** What a teammate may do on one turn. A decision zone: approve, send back, or hand it to a person. A work zone: pick
16
+ * where it goes, ask its person, or say it can't. v94: "use_tool" asks for one tool call. */
17
+ export declare const TURN_ACTIONS: readonly ["approve", "send_back", "hand_off", "route", "ask", "cant", "use_tool"];
18
+ export type TurnAction = (typeof TURN_ACTIONS)[number];
19
+ export declare const teammateTurnSchema: z.ZodObject<{
20
+ action: z.ZodEnum<{
21
+ approve: "approve";
22
+ ask: "ask";
23
+ cant: "cant";
24
+ hand_off: "hand_off";
25
+ route: "route";
26
+ send_back: "send_back";
27
+ use_tool: "use_tool";
28
+ }>;
29
+ answer: z.ZodString;
30
+ text: z.ZodString;
31
+ note: z.ZodString;
32
+ question: z.ZodString;
33
+ options: z.ZodArray<z.ZodString>;
34
+ reason: z.ZodString;
35
+ tool: z.ZodString;
36
+ input: z.ZodString;
37
+ remember: z.ZodString;
38
+ }, z.core.$strip>;
39
+ export type TurnAnswer = z.infer<typeof teammateTurnSchema>;
40
+ /** What Claude's `--json-schema` is for a turn: the turn schema, exactly. */
41
+ export declare const TURN_MODEL_SCHEMA: Readonly<Record<string, unknown>>;
42
+ /** Older and plain-text turns default absent or non-text fields to "", and keep only string options. */
43
+ export declare function readTurnAnswer(value: unknown): ContractResult<TurnAnswer>;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * A teammate's turn (teammates.ts): one schema for the JSON Schema Claude answers in (`--json-schema`) and the reader
3
+ * that takes the answer back. One flat object with every field (a root union is refused by the API); fields a turn
4
+ * does not use are "" (or [] for options).
5
+ *
6
+ * The text limits (TURN_LIMITS, from TEXT_LIMITS) are deliberately not in the schema: the CLI refuses an answer a few
7
+ * characters over one whole, so they are checked after parsing and an answer over one is asked once to shorten
8
+ * (teammate-work.ts), then kept whole. What the zone allows — which actions, which answers, a note for a send-back, a
9
+ * question for an ask, a tool for a tool call — is checked after parsing too (`readTurn`), by path.
10
+ *
11
+ * A turn is kept as the step's decision (`flow_step_run.decision_json`) as it was read, so a saved one is this shape.
12
+ */
13
+ import { z } from "zod";
14
+ import { parseContract, toModelSchema } from "./contract.js";
15
+ /** What a teammate may do on one turn. A decision zone: approve, send back, or hand it to a person. A work zone: pick
16
+ * where it goes, ask its person, or say it can't. v94: "use_tool" asks for one tool call. */
17
+ export const TURN_ACTIONS = ["approve", "send_back", "hand_off", "route", "ask", "cant", "use_tool"];
18
+ export const teammateTurnSchema = z.object({
19
+ action: z.enum(TURN_ACTIONS),
20
+ /** route: one of the zone's answers, exactly as written. */
21
+ answer: z.string(),
22
+ /** What the next zones read, or the draft as rewritten. */
23
+ text: z.string(),
24
+ /** A decision or send-back note. */
25
+ note: z.string(),
26
+ /** ask: one short question, with up to 4 options to tap. */
27
+ question: z.string(),
28
+ options: z.array(z.string()),
29
+ /** One plain sentence saying why, for its manager's log. */
30
+ reason: z.string(),
31
+ /** use_tool: the tool's name, and its input as a JSON object written as text. */
32
+ tool: z.string(),
33
+ input: z.string(),
34
+ /** v95: one short fact worth keeping for later cards ("": none). */
35
+ remember: z.string(),
36
+ });
37
+ /** What Claude's `--json-schema` is for a turn: the turn schema, exactly. */
38
+ export const TURN_MODEL_SCHEMA = toModelSchema(teammateTurnSchema);
39
+ /** Older and plain-text turns default absent or non-text fields to "", and keep only string options. */
40
+ export function readTurnAnswer(value) {
41
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
42
+ const body = { ...value };
43
+ for (const key of Object.keys(teammateTurnSchema.shape)) {
44
+ if (key !== "action" && key !== "options" && typeof body[key] !== "string")
45
+ body[key] = "";
46
+ }
47
+ body["options"] = Array.isArray(body["options"]) ? body["options"].filter(one => typeof one === "string") : [];
48
+ value = body;
49
+ }
50
+ return parseContract(teammateTurnSchema, value);
51
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * A Teams button tap (teams-chat.ts `receiveTeams`): an Adaptive Card `Action.Submit` arrives as a message activity
3
+ * whose `value` is the data Toolroll put on the button, `{ so: <one-time token> }`. The activity is Microsoft's (read
4
+ * by `receiveTeams`); only `value.so` is read, as before, and extra submit keys are ignored. A tap from the paired person whose data can't be read
5
+ * is answered with why (`value.so: must be a Toolroll button token`), and nothing is done.
6
+ */
7
+ import { z } from "zod";
8
+ import { type ContractResult } from "./contract.js";
9
+ export declare const teamsSubmitSchema: z.ZodObject<{
10
+ so: z.ZodString;
11
+ }, z.core.$strip>;
12
+ export type TeamsSubmit = z.infer<typeof teamsSubmitSchema>;
13
+ /** A submit's one-time token. */
14
+ export declare function readTeamsSubmit(value: unknown): ContractResult<string>;