toolroll 0.9.34 → 0.9.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +102 -0
- package/README.md +7 -0
- package/dist/assignment-adapters.d.ts +12 -48
- package/dist/assignment-adapters.js +33 -43
- package/dist/assignment.d.ts +1 -0
- package/dist/assignment.js +2 -2
- package/dist/browser-workspace.d.ts +3 -3
- package/dist/build-review.d.ts +1 -1
- package/dist/build-review.js +2 -1
- package/dist/builder.js +4 -7
- package/dist/chat-acceptance.d.ts +2 -2
- package/dist/chat-actions.d.ts +18 -12
- package/dist/chat-actions.js +15 -65
- package/dist/chat-ask.js +5 -5
- package/dist/chat-decide.d.ts +14 -1
- package/dist/chat-decide.js +12 -6
- package/dist/chat-delivery-state.d.ts +12 -70
- package/dist/chat-delivery-state.js +17 -135
- package/dist/chat-delivery.js +58 -30
- package/dist/chat-flow.js +8 -6
- package/dist/chat-inbox.js +1 -1
- package/dist/coding-context.d.ts +2 -39
- package/dist/coding-context.js +2 -1
- package/dist/coding-handoff.d.ts +6 -25
- package/dist/coding-handoff.js +11 -5
- package/dist/coding-types.d.ts +2 -26
- package/dist/coding-workspace.d.ts +2 -0
- package/dist/coding-workspace.js +50 -14
- package/dist/contracts/acceptance-terms.d.ts +14 -0
- package/dist/contracts/acceptance-terms.js +16 -0
- package/dist/contracts/chat-actions.d.ts +328 -0
- package/dist/contracts/chat-actions.js +133 -0
- package/dist/contracts/chat-callback-rows.d.ts +59 -0
- package/dist/contracts/chat-callback-rows.js +46 -0
- package/dist/contracts/chat-content.d.ts +276 -0
- package/dist/contracts/chat-content.js +127 -0
- package/dist/contracts/chat-tables.d.ts +6 -0
- package/dist/contracts/chat-tables.js +136 -0
- package/dist/contracts/coding-context.d.ts +97 -0
- package/dist/contracts/coding-context.js +60 -0
- package/dist/contracts/coding-handoff.d.ts +44 -0
- package/dist/contracts/coding-handoff.js +47 -0
- package/dist/contracts/coding-workspace.d.ts +165 -0
- package/dist/contracts/coding-workspace.js +71 -0
- package/dist/contracts/contract.d.ts +40 -7
- package/dist/contracts/contract.js +155 -19
- package/dist/contracts/decision.d.ts +45 -0
- package/dist/contracts/decision.js +45 -0
- package/dist/contracts/discord-callback.d.ts +45 -0
- package/dist/contracts/discord-callback.js +34 -0
- package/dist/contracts/flow-card.d.ts +50 -0
- package/dist/contracts/flow-card.js +46 -0
- package/dist/contracts/flow-propose.d.ts +325 -0
- package/dist/contracts/flow-propose.js +41 -0
- package/dist/contracts/flow-send.d.ts +119 -0
- package/dist/contracts/flow-send.js +112 -0
- package/dist/contracts/flow.d.ts +19405 -0
- package/dist/contracts/flow.js +364 -0
- package/dist/contracts/gateway-tools.d.ts +374 -0
- package/dist/contracts/gateway-tools.js +94 -0
- package/dist/contracts/handoff.d.ts +91 -0
- package/dist/contracts/handoff.js +82 -0
- package/dist/contracts/lead-context.d.ts +165 -0
- package/dist/contracts/lead-context.js +73 -0
- package/dist/contracts/lead-tools.d.ts +1905 -0
- package/dist/contracts/lead-tools.js +265 -0
- package/dist/contracts/memory-pass.d.ts +158 -0
- package/dist/contracts/memory-pass.js +102 -0
- package/dist/contracts/project-knowledge.d.ts +126 -0
- package/dist/contracts/project-knowledge.js +93 -0
- package/dist/contracts/project-memory.d.ts +117 -0
- package/dist/contracts/project-memory.js +51 -0
- package/dist/contracts/project-skills.d.ts +121 -0
- package/dist/contracts/project-skills.js +76 -0
- package/dist/contracts/proof.d.ts +136 -0
- package/dist/contracts/proof.js +110 -0
- package/dist/contracts/review-findings.d.ts +46 -0
- package/dist/contracts/review-findings.js +48 -0
- package/dist/contracts/route.d.ts +484 -0
- package/dist/contracts/route.js +128 -0
- package/dist/contracts/scope.d.ts +105 -0
- package/dist/contracts/scope.js +114 -0
- package/dist/contracts/scout-report.d.ts +234 -0
- package/dist/contracts/scout-report.js +141 -0
- package/dist/contracts/slack-callback.d.ts +43 -0
- package/dist/contracts/slack-callback.js +35 -0
- package/dist/contracts/sort-answer.d.ts +83 -0
- package/dist/contracts/sort-answer.js +77 -0
- package/dist/contracts/stage-output.d.ts +127 -0
- package/dist/contracts/stage-output.js +166 -0
- package/dist/contracts/task-sizing.d.ts +24 -0
- package/dist/contracts/task-sizing.js +28 -0
- package/dist/contracts/teammate-turn.d.ts +43 -0
- package/dist/contracts/teammate-turn.js +51 -0
- package/dist/contracts/teams-callback.d.ts +14 -0
- package/dist/contracts/teams-callback.js +14 -0
- package/dist/contracts/telegram-callback.d.ts +120 -0
- package/dist/contracts/telegram-callback.js +97 -0
- package/dist/contracts/verification-receipt.d.ts +148 -0
- package/dist/contracts/verification-receipt.js +87 -0
- package/dist/decision.d.ts +11 -20
- package/dist/decision.js +86 -167
- package/dist/demo.js +28 -11
- package/dist/discord-chat.js +31 -20
- package/dist/evidence.d.ts +3 -40
- package/dist/evidence.js +5 -1
- package/dist/flow-engine.js +14 -17
- package/dist/flow-gallery.d.ts +3 -2
- package/dist/flow-items.d.ts +4 -13
- package/dist/flow-items.js +1 -15
- package/dist/flow-send.d.ts +6 -37
- package/dist/flow-send.js +18 -14
- package/dist/flow-share.d.ts +10 -32
- package/dist/flow-share.js +72 -84
- package/dist/flow-sort.d.ts +7 -26
- package/dist/flow-sort.js +25 -20
- package/dist/flow-starters.d.ts +2 -1
- package/dist/flow-steps.js +3 -2
- package/dist/flow-triggers.d.ts +15 -72
- package/dist/flow-triggers.js +94 -83
- package/dist/flows.d.ts +37 -220
- package/dist/flows.js +356 -387
- package/dist/invoke.js +4 -4
- package/dist/lead-context.d.ts +4 -2
- package/dist/lead-context.js +24 -17
- package/dist/mate-tools.d.ts +4 -0
- package/dist/mate-tools.js +111 -308
- package/dist/mcp-connect.d.ts +27 -4
- package/dist/mcp-connect.js +92 -17
- package/dist/mcp.js +84 -216
- package/dist/memory-pass.d.ts +17 -42
- package/dist/memory-pass.js +54 -26
- package/dist/operate.js +31 -2
- package/dist/phase-routing.d.ts +19 -72
- package/dist/phase-routing.js +34 -164
- package/dist/plan.d.ts +5 -5
- package/dist/plan.js +5 -25
- package/dist/process-recovery-eligibility.d.ts +2 -2
- package/dist/process-recovery-eligibility.js +13 -8
- package/dist/project-knowledge.d.ts +16 -35
- package/dist/project-knowledge.js +42 -21
- package/dist/project-memory.d.ts +2 -28
- package/dist/project-memory.js +28 -13
- package/dist/project-skills.d.ts +16 -38
- package/dist/project-skills.js +41 -32
- package/dist/project-tools.d.ts +1 -0
- package/dist/project-tools.js +4 -4
- package/dist/proof.d.ts +15 -91
- package/dist/proof.js +90 -249
- package/dist/provider.d.ts +2 -0
- package/dist/provider.js +4 -16
- package/dist/result-evidence-readers.d.ts +42 -0
- package/dist/result-evidence-readers.js +91 -0
- package/dist/result-shots.d.ts +1 -1
- package/dist/result-shots.js +2 -1
- package/dist/review-switch.d.ts +3 -8
- package/dist/reviewer.d.ts +8 -12
- package/dist/reviewer.js +11 -35
- package/dist/scope.d.ts +21 -43
- package/dist/scope.js +42 -128
- package/dist/scout-report.d.ts +9 -194
- package/dist/scout-report.js +113 -264
- package/dist/scout.js +15 -11
- package/dist/serve.js +17 -101
- package/dist/slack-chat.js +26 -20
- package/dist/slack-state.js +2 -1
- package/dist/store.d.ts +9 -32
- package/dist/store.js +18 -15
- package/dist/structured-output.js +2 -1
- package/dist/subscription-chat.d.ts +4 -0
- package/dist/subscription-chat.js +4 -1
- package/dist/surface.js +8 -1
- package/dist/task-review-brief.d.ts +69 -0
- package/dist/task-review-brief.js +179 -0
- package/dist/task-sizing.d.ts +3 -27
- package/dist/task-sizing.js +11 -26
- package/dist/task-text.d.ts +0 -5
- package/dist/task-text.js +1 -3
- package/dist/teammate-question.js +7 -5
- package/dist/teammate-work.js +9 -5
- package/dist/teammates.d.ts +13 -64
- package/dist/teammates.js +32 -33
- package/dist/teams-chat.js +20 -11
- package/dist/telegram-flow.js +4 -3
- package/dist/telegram-mate.d.ts +4 -14
- package/dist/telegram-mate.js +8 -7
- package/dist/telegram-team.d.ts +2 -19
- package/dist/telegram.d.ts +1 -1
- package/dist/telegram.js +52 -42
- package/dist/text-limits.d.ts +212 -1
- package/dist/text-limits.js +214 -1
- package/dist/tools-ui.d.ts +2 -1
- package/dist/tools-ui.js +7 -4
- package/dist/verification-evidence.js +24 -12
- package/package.json +1 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scout's report: one schema for the report a scout writes (its report file, or Claude's structured output), the
|
|
3
|
+
* JSON Schema a Claude scout is given (`--json-schema`, `SCOUT_OUTPUT_JSON_SCHEMA`) and the reader that takes it back
|
|
4
|
+
* (`parseReport` in scout-report.ts), which also reads every report already kept as evidence. The rules JSON Schema
|
|
5
|
+
* cannot state — UTF-8 byte limits, blank text, control characters, one-line titles, a one-paragraph summary, safe
|
|
6
|
+
* links, duplicate screenshots and items naming them — run in plain code after parsing, each with a path-named error.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { limitRule, TEXT_LIMITS } from "../text-limits.js";
|
|
10
|
+
import { limited, readVersioned, toModelSchema, versioned } from "./contract.js";
|
|
11
|
+
/** How many follow-ups, items and screenshots a report may carry. */
|
|
12
|
+
export const REPORT_COUNTS = { followUps: 5, items: 6, images: 8 };
|
|
13
|
+
/** Caps are BYTES of UTF-8 (v4 review, finding 11): a 64 KiB report is 64 KiB whatever script it is written in. They
|
|
14
|
+
* hold what a scout writes; a stored report is read without them (`storedReportSchema`). */
|
|
15
|
+
export const REPORT_LIMITS = {
|
|
16
|
+
payload: TEXT_LIMITS.reportPayloadBytes,
|
|
17
|
+
title: TEXT_LIMITS.reportTitleBytes,
|
|
18
|
+
summary: TEXT_LIMITS.reportSummary,
|
|
19
|
+
document: TEXT_LIMITS.reportDocumentBytes,
|
|
20
|
+
followUps: REPORT_COUNTS.followUps,
|
|
21
|
+
followUpTitle: TEXT_LIMITS.reportTitleBytes,
|
|
22
|
+
/** A follow-up files as a task: its goal is held to the task goal limit, in bytes of the same count. */
|
|
23
|
+
followUpGoal: TEXT_LIMITS.goal,
|
|
24
|
+
items: REPORT_COUNTS.items,
|
|
25
|
+
itemTitle: TEXT_LIMITS.reportTitleBytes,
|
|
26
|
+
itemWhy: TEXT_LIMITS.reportWhyBytes,
|
|
27
|
+
url: TEXT_LIMITS.reportUrlBytes,
|
|
28
|
+
images: REPORT_COUNTS.images,
|
|
29
|
+
caption: TEXT_LIMITS.reportCaptionBytes,
|
|
30
|
+
};
|
|
31
|
+
/** A screenshot's name in the scout's output folder: one plain file name, PNG or JPEG, never a path. */
|
|
32
|
+
export const REPORT_IMAGE_FILE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}\.(?:png|jpe?g)$/i;
|
|
33
|
+
/** Each text field, the limit it is written within, and whether it is one line or a web address. */
|
|
34
|
+
export const REPORT_TEXT_FIELDS = {
|
|
35
|
+
title: { limit: "reportTitleBytes", oneLine: true },
|
|
36
|
+
summary: { limit: "reportSummary" },
|
|
37
|
+
report: { limit: "reportDocumentBytes" },
|
|
38
|
+
};
|
|
39
|
+
export const FOLLOW_UP_TEXT_FIELDS = { title: { limit: "reportTitleBytes", oneLine: true }, goal: { limit: "goal" } };
|
|
40
|
+
export const ITEM_TEXT_FIELDS = { title: { limit: "reportTitleBytes", oneLine: true }, why: { limit: "reportWhyBytes" }, url: { limit: "reportUrlBytes", link: true } };
|
|
41
|
+
// The image's file is any one line here: the runner refuses one that isn't a plain file name in its output folder, not the report.
|
|
42
|
+
export const IMAGE_TEXT_FIELDS = { file: { limit: "reportCaptionBytes", oneLine: true }, caption: { limit: "reportCaptionBytes", oneLine: true }, url: { limit: "reportUrlBytes", link: true } };
|
|
43
|
+
export const REPORT_VERSION = 1;
|
|
44
|
+
function reportSchemaOf(text) {
|
|
45
|
+
// Limits named without "Bytes" that a report still counts in bytes: the model is told so.
|
|
46
|
+
const bytes = (field, limit) => text(field, limit).describe(limitRule(field, TEXT_LIMITS[limit], "bytes"));
|
|
47
|
+
const followUp = z.strictObject({
|
|
48
|
+
title: text("follow-up title", "reportTitleBytes").min(1),
|
|
49
|
+
goal: bytes("follow-up goal", "goal").min(1),
|
|
50
|
+
});
|
|
51
|
+
const item = z.strictObject({
|
|
52
|
+
title: text("item title", "reportTitleBytes").min(1),
|
|
53
|
+
why: text("item why", "reportWhyBytes").min(1),
|
|
54
|
+
url: text("item url", "reportUrlBytes").min(1),
|
|
55
|
+
image: z.string().nullable().optional().describe("the file of the one of images this item shows, if any"),
|
|
56
|
+
});
|
|
57
|
+
const image = z.strictObject({
|
|
58
|
+
file: text("image file", "reportCaptionBytes").min(1),
|
|
59
|
+
caption: text("image caption", "reportCaptionBytes").min(1),
|
|
60
|
+
url: text("image url", "reportUrlBytes").min(1),
|
|
61
|
+
/** Added by Toolroll once the screenshot is stored: its bytes' sha256 and the evidence that holds them. */
|
|
62
|
+
sha256: z.string().regex(/^[0-9a-f]{64}$/, { error: "must be a sha256 hex digest" }).optional().describe("set by Toolroll when it keeps the screenshot; leave out"),
|
|
63
|
+
artifact: z.int().positive().optional().describe("set by Toolroll when it keeps the screenshot; leave out"),
|
|
64
|
+
});
|
|
65
|
+
return versioned(REPORT_VERSION, {
|
|
66
|
+
title: text("title", "reportTitleBytes").min(1),
|
|
67
|
+
summary: bytes("summary", "reportSummary").min(1),
|
|
68
|
+
report: text("report", "reportDocumentBytes").min(1),
|
|
69
|
+
followUps: z.array(followUp).max(REPORT_COUNTS.followUps).optional(),
|
|
70
|
+
items: z.array(item).max(REPORT_COUNTS.items).optional(),
|
|
71
|
+
images: z.array(image).max(REPORT_COUNTS.images).optional(),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/** A report as a scout writes it now. */
|
|
75
|
+
export const scoutReportSchema = reportSchemaOf(limited);
|
|
76
|
+
/** A report kept as evidence: the same, except text keeps the length it was stored with. */
|
|
77
|
+
export const storedReportSchema = reportSchemaOf(() => z.string());
|
|
78
|
+
/** The park mailbox's decision as a scout hands it back: the same fields `parseDecision` reads, which stays its
|
|
79
|
+
* validator until the decision contract (docs/plans/zod-revamp.md, item 7) gives it one schema. */
|
|
80
|
+
export const scoutDecisionSchema = z.strictObject({
|
|
81
|
+
urgency: z.literal("blocking"),
|
|
82
|
+
recap: z.string(),
|
|
83
|
+
question: z.string(),
|
|
84
|
+
options: z.array(z.strictObject({ id: z.string(), label: z.string(), consequence: z.string(), reversible: z.boolean() })),
|
|
85
|
+
recommendation: z.string(),
|
|
86
|
+
});
|
|
87
|
+
/**
|
|
88
|
+
* The scout's handback for Claude's `--json-schema` (run 2334's fix): plan mode only lets a session write its own plan
|
|
89
|
+
* file, so a Claude scout returns a report — or a question for the operator — as structured output on the terminal
|
|
90
|
+
* result event. The report branch is the report schema itself; the byte limits and rules JSON Schema cannot state are
|
|
91
|
+
* `parseReport`'s (and `parseDecision`'s), applied to the branch as handed back.
|
|
92
|
+
*/
|
|
93
|
+
export const scoutHandbackSchema = z.strictObject({
|
|
94
|
+
kind: z.enum(["report", "question"]),
|
|
95
|
+
report: scoutReportSchema.optional(),
|
|
96
|
+
decision: scoutDecisionSchema.optional(),
|
|
97
|
+
});
|
|
98
|
+
export const SCOUT_OUTPUT_JSON_SCHEMA = toModelSchema(scoutHandbackSchema);
|
|
99
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
100
|
+
function pick(body, fields) {
|
|
101
|
+
const out = {};
|
|
102
|
+
for (const field of fields) {
|
|
103
|
+
if (Object.prototype.hasOwnProperty.call(body, field) && body[field] !== undefined)
|
|
104
|
+
out[field] = body[field];
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
const ENTRY_FIELDS = {
|
|
109
|
+
followUps: Object.keys(FOLLOW_UP_TEXT_FIELDS),
|
|
110
|
+
items: [...Object.keys(ITEM_TEXT_FIELDS), "image"],
|
|
111
|
+
images: [...Object.keys(IMAGE_TEXT_FIELDS), "sha256", "artifact"],
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Known reports keep the old parser's field-picking, whether unversioned (including `version: null`) or version 1:
|
|
115
|
+
* unknown fields in the report, follow-ups, items and images are ignored, and a null list means none. Known field
|
|
116
|
+
* values are never rewritten, and anything that is not an object is left for the schema to refuse by path.
|
|
117
|
+
*/
|
|
118
|
+
function normalizeReportV1(body) {
|
|
119
|
+
const out = { version: REPORT_VERSION, ...pick(body, [...Object.keys(REPORT_TEXT_FIELDS), "followUps", "items", "images"]) };
|
|
120
|
+
for (const list of ["followUps", "items", "images"]) {
|
|
121
|
+
if (out[list] === null)
|
|
122
|
+
delete out[list];
|
|
123
|
+
else if (Array.isArray(out[list]))
|
|
124
|
+
out[list] = out[list].map(entry => (isRecord(entry) ? pick(entry, ENTRY_FIELDS[list]) : entry));
|
|
125
|
+
}
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
export const REPORT_UPGRADES = { 0: normalizeReportV1 };
|
|
129
|
+
/** Normalize current and unversioned reports; leave other versions for readVersioned to check. */
|
|
130
|
+
export function reportPayloadBody(input) {
|
|
131
|
+
if (!isRecord(input))
|
|
132
|
+
return null;
|
|
133
|
+
return !Object.prototype.hasOwnProperty.call(input, "version") || input["version"] === null || input["version"] === REPORT_VERSION
|
|
134
|
+
? normalizeReportV1(input)
|
|
135
|
+
: input;
|
|
136
|
+
}
|
|
137
|
+
/** Read current and unversioned reports with the same field-picking, then the strict schema; refuse newer versions
|
|
138
|
+
* plainly. `stored`: one kept as evidence, read without the length limits. */
|
|
139
|
+
export function readReportPayload(input, options = {}) {
|
|
140
|
+
return readVersioned(options.stored === true ? storedReportSchema : scoutReportSchema, reportPayloadBody(input) ?? input, REPORT_UPGRADES);
|
|
141
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
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 { type ContractResult } from "./contract.js";
|
|
9
|
+
/** Toolroll's buttons: toolroll_* since the rename; standing_orders_* buttons on older messages still work. */
|
|
10
|
+
export declare const SLACK_BUTTON_ACTION: RegExp;
|
|
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 declare const SLACK_LINK_ACTION: RegExp;
|
|
13
|
+
/** The interaction around a tap: who, and the message the button is on. */
|
|
14
|
+
export declare const slackBlockActionsSchema: z.ZodObject<{
|
|
15
|
+
type: z.ZodLiteral<"block_actions">;
|
|
16
|
+
user: z.ZodObject<{
|
|
17
|
+
id: z.ZodString;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
container: z.ZodObject<{
|
|
20
|
+
type: z.ZodString;
|
|
21
|
+
channel_id: z.ZodOptional<z.ZodString>;
|
|
22
|
+
message_ts: z.ZodOptional<z.ZodString>;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
message: z.ZodOptional<z.ZodObject<{
|
|
25
|
+
thread_ts: z.ZodOptional<z.ZodString>;
|
|
26
|
+
}, z.core.$strip>>;
|
|
27
|
+
actions: z.ZodArray<z.ZodObject<{
|
|
28
|
+
action_id: z.ZodOptional<z.ZodString>;
|
|
29
|
+
value: z.ZodOptional<z.ZodString>;
|
|
30
|
+
action_ts: z.ZodOptional<z.ZodString>;
|
|
31
|
+
}, z.core.$strip>>;
|
|
32
|
+
}, z.core.$strip>;
|
|
33
|
+
export type SlackBlockActions = z.infer<typeof slackBlockActionsSchema>;
|
|
34
|
+
/** The one tapped button, as Toolroll made it. */
|
|
35
|
+
export declare const slackButtonSchema: z.ZodObject<{
|
|
36
|
+
action_id: z.ZodString;
|
|
37
|
+
value: z.ZodString;
|
|
38
|
+
action_ts: z.ZodString;
|
|
39
|
+
}, z.core.$strip>;
|
|
40
|
+
export type SlackButton = z.infer<typeof slackButtonSchema>;
|
|
41
|
+
export declare function readSlackBlockActions(body: unknown): ContractResult<SlackBlockActions>;
|
|
42
|
+
/** The tapped button: exactly one, Toolroll's (`actions[0].action_id: not a Toolroll button`). */
|
|
43
|
+
export declare function readSlackButton(interaction: SlackBlockActions): ContractResult<SlackButton>;
|
|
@@ -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[];
|