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.
Files changed (195) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/README.md +7 -0
  3. package/dist/assignment-adapters.d.ts +12 -48
  4. package/dist/assignment-adapters.js +33 -43
  5. package/dist/assignment.d.ts +1 -0
  6. package/dist/assignment.js +2 -2
  7. package/dist/browser-workspace.d.ts +3 -3
  8. package/dist/build-review.d.ts +1 -1
  9. package/dist/build-review.js +2 -1
  10. package/dist/builder.js +4 -7
  11. package/dist/chat-acceptance.d.ts +2 -2
  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-decide.d.ts +14 -1
  16. package/dist/chat-decide.js +12 -6
  17. package/dist/chat-delivery-state.d.ts +12 -70
  18. package/dist/chat-delivery-state.js +17 -135
  19. package/dist/chat-delivery.js +58 -30
  20. package/dist/chat-flow.js +8 -6
  21. package/dist/chat-inbox.js +1 -1
  22. package/dist/coding-context.d.ts +2 -39
  23. package/dist/coding-context.js +2 -1
  24. package/dist/coding-handoff.d.ts +6 -25
  25. package/dist/coding-handoff.js +11 -5
  26. package/dist/coding-types.d.ts +2 -26
  27. package/dist/coding-workspace.d.ts +2 -0
  28. package/dist/coding-workspace.js +50 -14
  29. package/dist/contracts/acceptance-terms.d.ts +14 -0
  30. package/dist/contracts/acceptance-terms.js +16 -0
  31. package/dist/contracts/chat-actions.d.ts +328 -0
  32. package/dist/contracts/chat-actions.js +133 -0
  33. package/dist/contracts/chat-callback-rows.d.ts +59 -0
  34. package/dist/contracts/chat-callback-rows.js +46 -0
  35. package/dist/contracts/chat-content.d.ts +276 -0
  36. package/dist/contracts/chat-content.js +127 -0
  37. package/dist/contracts/chat-tables.d.ts +6 -0
  38. package/dist/contracts/chat-tables.js +136 -0
  39. package/dist/contracts/coding-context.d.ts +97 -0
  40. package/dist/contracts/coding-context.js +60 -0
  41. package/dist/contracts/coding-handoff.d.ts +44 -0
  42. package/dist/contracts/coding-handoff.js +47 -0
  43. package/dist/contracts/coding-workspace.d.ts +165 -0
  44. package/dist/contracts/coding-workspace.js +71 -0
  45. package/dist/contracts/contract.d.ts +40 -7
  46. package/dist/contracts/contract.js +155 -19
  47. package/dist/contracts/decision.d.ts +45 -0
  48. package/dist/contracts/decision.js +45 -0
  49. package/dist/contracts/discord-callback.d.ts +45 -0
  50. package/dist/contracts/discord-callback.js +34 -0
  51. package/dist/contracts/flow-card.d.ts +50 -0
  52. package/dist/contracts/flow-card.js +46 -0
  53. package/dist/contracts/flow-propose.d.ts +325 -0
  54. package/dist/contracts/flow-propose.js +41 -0
  55. package/dist/contracts/flow-send.d.ts +119 -0
  56. package/dist/contracts/flow-send.js +112 -0
  57. package/dist/contracts/flow.d.ts +19405 -0
  58. package/dist/contracts/flow.js +364 -0
  59. package/dist/contracts/gateway-tools.d.ts +374 -0
  60. package/dist/contracts/gateway-tools.js +94 -0
  61. package/dist/contracts/handoff.d.ts +91 -0
  62. package/dist/contracts/handoff.js +82 -0
  63. package/dist/contracts/lead-context.d.ts +165 -0
  64. package/dist/contracts/lead-context.js +73 -0
  65. package/dist/contracts/lead-tools.d.ts +1905 -0
  66. package/dist/contracts/lead-tools.js +265 -0
  67. package/dist/contracts/memory-pass.d.ts +158 -0
  68. package/dist/contracts/memory-pass.js +102 -0
  69. package/dist/contracts/project-knowledge.d.ts +126 -0
  70. package/dist/contracts/project-knowledge.js +93 -0
  71. package/dist/contracts/project-memory.d.ts +117 -0
  72. package/dist/contracts/project-memory.js +51 -0
  73. package/dist/contracts/project-skills.d.ts +121 -0
  74. package/dist/contracts/project-skills.js +76 -0
  75. package/dist/contracts/proof.d.ts +136 -0
  76. package/dist/contracts/proof.js +110 -0
  77. package/dist/contracts/review-findings.d.ts +46 -0
  78. package/dist/contracts/review-findings.js +48 -0
  79. package/dist/contracts/route.d.ts +484 -0
  80. package/dist/contracts/route.js +128 -0
  81. package/dist/contracts/scope.d.ts +105 -0
  82. package/dist/contracts/scope.js +114 -0
  83. package/dist/contracts/scout-report.d.ts +234 -0
  84. package/dist/contracts/scout-report.js +141 -0
  85. package/dist/contracts/slack-callback.d.ts +43 -0
  86. package/dist/contracts/slack-callback.js +35 -0
  87. package/dist/contracts/sort-answer.d.ts +83 -0
  88. package/dist/contracts/sort-answer.js +77 -0
  89. package/dist/contracts/stage-output.d.ts +127 -0
  90. package/dist/contracts/stage-output.js +166 -0
  91. package/dist/contracts/task-sizing.d.ts +24 -0
  92. package/dist/contracts/task-sizing.js +28 -0
  93. package/dist/contracts/teammate-turn.d.ts +43 -0
  94. package/dist/contracts/teammate-turn.js +51 -0
  95. package/dist/contracts/teams-callback.d.ts +14 -0
  96. package/dist/contracts/teams-callback.js +14 -0
  97. package/dist/contracts/telegram-callback.d.ts +120 -0
  98. package/dist/contracts/telegram-callback.js +97 -0
  99. package/dist/contracts/verification-receipt.d.ts +148 -0
  100. package/dist/contracts/verification-receipt.js +87 -0
  101. package/dist/decision.d.ts +11 -20
  102. package/dist/decision.js +86 -167
  103. package/dist/demo.js +28 -11
  104. package/dist/discord-chat.js +31 -20
  105. package/dist/evidence.d.ts +3 -40
  106. package/dist/evidence.js +5 -1
  107. package/dist/flow-engine.js +14 -17
  108. package/dist/flow-gallery.d.ts +3 -2
  109. package/dist/flow-items.d.ts +4 -13
  110. package/dist/flow-items.js +1 -15
  111. package/dist/flow-send.d.ts +6 -37
  112. package/dist/flow-send.js +18 -14
  113. package/dist/flow-share.d.ts +10 -32
  114. package/dist/flow-share.js +72 -84
  115. package/dist/flow-sort.d.ts +7 -26
  116. package/dist/flow-sort.js +25 -20
  117. package/dist/flow-starters.d.ts +2 -1
  118. package/dist/flow-steps.js +3 -2
  119. package/dist/flow-triggers.d.ts +15 -72
  120. package/dist/flow-triggers.js +94 -83
  121. package/dist/flows.d.ts +37 -220
  122. package/dist/flows.js +356 -387
  123. package/dist/invoke.js +4 -4
  124. package/dist/lead-context.d.ts +4 -2
  125. package/dist/lead-context.js +24 -17
  126. package/dist/mate-tools.d.ts +4 -0
  127. package/dist/mate-tools.js +111 -308
  128. package/dist/mcp-connect.d.ts +27 -4
  129. package/dist/mcp-connect.js +92 -17
  130. package/dist/mcp.js +84 -216
  131. package/dist/memory-pass.d.ts +17 -42
  132. package/dist/memory-pass.js +54 -26
  133. package/dist/operate.js +31 -2
  134. package/dist/phase-routing.d.ts +19 -72
  135. package/dist/phase-routing.js +34 -164
  136. package/dist/plan.d.ts +5 -5
  137. package/dist/plan.js +5 -25
  138. package/dist/process-recovery-eligibility.d.ts +2 -2
  139. package/dist/process-recovery-eligibility.js +13 -8
  140. package/dist/project-knowledge.d.ts +16 -35
  141. package/dist/project-knowledge.js +42 -21
  142. package/dist/project-memory.d.ts +2 -28
  143. package/dist/project-memory.js +28 -13
  144. package/dist/project-skills.d.ts +16 -38
  145. package/dist/project-skills.js +41 -32
  146. package/dist/project-tools.d.ts +1 -0
  147. package/dist/project-tools.js +4 -4
  148. package/dist/proof.d.ts +15 -91
  149. package/dist/proof.js +90 -249
  150. package/dist/provider.d.ts +2 -0
  151. package/dist/provider.js +4 -16
  152. package/dist/result-evidence-readers.d.ts +42 -0
  153. package/dist/result-evidence-readers.js +91 -0
  154. package/dist/result-shots.d.ts +1 -1
  155. package/dist/result-shots.js +2 -1
  156. package/dist/review-switch.d.ts +3 -8
  157. package/dist/reviewer.d.ts +8 -12
  158. package/dist/reviewer.js +11 -35
  159. package/dist/scope.d.ts +21 -43
  160. package/dist/scope.js +42 -128
  161. package/dist/scout-report.d.ts +9 -194
  162. package/dist/scout-report.js +113 -264
  163. package/dist/scout.js +15 -11
  164. package/dist/serve.js +17 -101
  165. package/dist/slack-chat.js +26 -20
  166. package/dist/slack-state.js +2 -1
  167. package/dist/store.d.ts +9 -32
  168. package/dist/store.js +18 -15
  169. package/dist/structured-output.js +2 -1
  170. package/dist/subscription-chat.d.ts +4 -0
  171. package/dist/subscription-chat.js +4 -1
  172. package/dist/surface.js +8 -1
  173. package/dist/task-review-brief.d.ts +69 -0
  174. package/dist/task-review-brief.js +179 -0
  175. package/dist/task-sizing.d.ts +3 -27
  176. package/dist/task-sizing.js +11 -26
  177. package/dist/task-text.d.ts +0 -5
  178. package/dist/task-text.js +1 -3
  179. package/dist/teammate-question.js +7 -5
  180. package/dist/teammate-work.js +9 -5
  181. package/dist/teammates.d.ts +13 -64
  182. package/dist/teammates.js +32 -33
  183. package/dist/teams-chat.js +20 -11
  184. package/dist/telegram-flow.js +4 -3
  185. package/dist/telegram-mate.d.ts +4 -14
  186. package/dist/telegram-mate.js +8 -7
  187. package/dist/telegram-team.d.ts +2 -19
  188. package/dist/telegram.d.ts +1 -1
  189. package/dist/telegram.js +52 -42
  190. package/dist/text-limits.d.ts +212 -1
  191. package/dist/text-limits.js +214 -1
  192. package/dist/tools-ui.d.ts +2 -1
  193. package/dist/tools-ui.js +7 -4
  194. package/dist/verification-evidence.js +24 -12
  195. package/package.json +1 -1
@@ -0,0 +1,265 @@
1
+ /**
2
+ * The lead's tools (docs/plans/zod-revamp.md, item 5): one input schema per tool, and one output schema for what each
3
+ * returns when it succeeds. The input schema supplies the JSON Schema the lead is given (`toModelSchema`) and the
4
+ * compatible reader used before any handler runs (mate-tools.ts), so a refusal names the paths it reads next step.
5
+ * What JSON Schema can't say — a repo the operator may reach, plain text without secrets, an ISO time, a decision read
6
+ * in an earlier step — is still checked in plain code after parsing, with its own words.
7
+ *
8
+ * Output schemas describe a result's top level. They are checked in tests and logged when a result disagrees; a call
9
+ * that succeeds is never refused because of one (an output schema is a record of the contract, not a gate).
10
+ */
11
+ import { z } from "zod";
12
+ import { CHAT_ACTIONS } from "../chat-actions.js";
13
+ import { RESULT_IMAGES_PER_TURN_CAP } from "../chat-evidence.js";
14
+ import { CHAT_CONTROLS } from "../chat-controls.js";
15
+ import { CHAT_TASK_ACTIONS } from "../chat-task-actions.js";
16
+ import { LIMITS } from "../decision.js";
17
+ import { CHECK_RESULTS, RUN_OUTCOMES, TASK_STATES } from "../lead-commitments.js";
18
+ import { PERSON_ID } from "../lead-people.js";
19
+ import { RISK_LEVELS, TASK_SIZES } from "../phase-routing.js";
20
+ import { ACCEPTANCE_LIMITS } from "../scope.js";
21
+ import { TEAMMATE_TEMPLATES } from "../teammates.js";
22
+ import { TEXT_LIMITS } from "../text-limits.js";
23
+ import { parseContract } from "./contract.js";
24
+ import { FLOW_ALIASES } from "./flow.js";
25
+ import { proposeFlowInputSchema } from "./flow-propose.js";
26
+ import { acceptanceCriterionSchema } from "./plan.js";
27
+ const enumOf = (values) => z.enum(values);
28
+ const text = (key) => z.string().max(TEXT_LIMITS[key]);
29
+ const id = z.int().min(1);
30
+ const offset = z.int().min(0);
31
+ /** Text a model is told is "refused with its length, not cut": the refusal says how long it was. */
32
+ function overBy(limit, input) {
33
+ const count = (n) => n.toLocaleString("en-US");
34
+ return `over ${count(limit)} characters (it is ${typeof input === "string" ? count(input.length) : "longer"}): shorten it and call again`;
35
+ }
36
+ /** A task as a tool names it: its id. */
37
+ export const taskRef = z.string().min(1).max(TEXT_LIMITS.taskRef);
38
+ /** A project as list_repos names it (r1, r2). */
39
+ export const repoId = z.string().regex(/^r[0-9]{1,3}$/);
40
+ /** A task's goal or what it leaves out: the length a model is told before it writes, and refused (not cut) beyond. */
41
+ export const scopeText = z.string().max(TEXT_LIMITS.goal, { error: issue => overBy(TEXT_LIMITS.goal, issue.input) })
42
+ .describe(`At most ${TEXT_LIMITS.goal.toLocaleString("en-US")} characters (UTF-16 code units) and ${TEXT_LIMITS.goalBytes.toLocaleString("en-US")} UTF-8 bytes; no control or disguised text. Longer text is refused with its length, not cut: shorten it and call again.`);
43
+ /** The paths a task expects to touch. */
44
+ export const touches = z.array(text("taskTouch")).max(50);
45
+ /** The rubric a task is accepted against: the plan's own criterion schema; duplicate ids and byte limits are checked after. */
46
+ export const acceptance = z.array(acceptanceCriterionSchema).min(1).max(ACCEPTANCE_LIMITS.criteria);
47
+ /** A note the operator's own words fill (revise feedback, steering, a flow card's note). */
48
+ const note = z.string().max(LIMITS.note, { error: issue => overBy(LIMITS.note, issue.input) }).describe(`At most ${LIMITS.note} characters; longer is refused with its length, not cut.`);
49
+ /** Task states a list filters by. */
50
+ export const LISTED_TASK_STATES = ["queued", "running", "done", "failed", "cancelled"];
51
+ /** The shared actions the lead proposes with propose_action; flows and teammates have their own tools. */
52
+ const ACTION_OPERATIONS = Object.keys(CHAT_ACTIONS).filter(one => !one.startsWith("flow_") && !one.startsWith("teammate_"));
53
+ export const LEAD_TOOL_INPUTS = {
54
+ get_brief: z.strictObject({ repo: repoId.optional() }),
55
+ get_project_context: z.strictObject({ repo: repoId, query: text("contextQuery").min(1), mode: z.enum(["search", "impact"]).optional() }),
56
+ get_action_status: z.strictObject({ proposal: id }),
57
+ get_actions: z.strictObject({}),
58
+ propose_action: z.strictObject({
59
+ operation: enumOf(ACTION_OPERATIONS), repo: z.string().optional(), task: taskRef.optional(), version: z.string().optional(), restore: id.optional(),
60
+ sample: text("actionSample").optional(), content: text("actionContent").optional(), instructions: text("flowInstructions").optional(),
61
+ title: text("actionTitle").optional(), id: z.string().optional(), run: id.optional(), note: note.optional(), catalog: text("actionName").optional(),
62
+ name: text("actionName").optional(), command: text("actionCommand").optional(), args: z.array(text("actionCommand")).max(40).optional(),
63
+ url: text("actionUrl").optional(), secrets: z.array(text("actionSecret")).max(12).optional(), about: text("actionAbout").optional(),
64
+ }),
65
+ propose_task_action: z.strictObject({ task: taskRef, operation: enumOf(Object.keys(CHAT_TASK_ACTIONS)), dependency: taskRef.optional(), run: id.optional() }),
66
+ get_controls: z.strictObject({}),
67
+ show_control: z.strictObject({ control: enumOf(Object.keys(CHAT_CONTROLS)), task: taskRef.optional(), repo: repoId.optional(), run: id.optional() }),
68
+ get_result: z.strictObject({ task: taskRef, run: id.optional(), feedback_offset: offset.optional() }),
69
+ get_diff: z.strictObject({ task: taskRef, run: id.optional(), file: text("diffPath").min(1).optional(), offset: offset.optional() }),
70
+ get_check_log: z.strictObject({ task: taskRef, run: id.optional(), search: text("logSearch").min(2).optional(), offset: offset.optional() }),
71
+ get_acceptance_evidence: z.strictObject({ task: taskRef, run: id.optional(), offset: offset.optional() }),
72
+ get_result_images: z.strictObject({ task: taskRef, run: id.optional(), offset: offset.optional(), images: z.array(id).min(1).max(RESULT_IMAGES_PER_TURN_CAP).optional() }),
73
+ propose_review: z.strictObject({
74
+ run: id, operation: z.enum(["note", "revise"]),
75
+ // A note, path or line left as null reads as left out, as it always has.
76
+ note: z.string().max(LIMITS.note).nullable().optional(), path: text("diffPath").nullable().optional(),
77
+ line: z.int().min(1).max(1_000_000).nullable().optional(),
78
+ saved_notes: z.array(id).max(100).optional(),
79
+ }),
80
+ recap: z.strictObject({ since: text("recapSince").optional() }),
81
+ list_repos: z.strictObject({}),
82
+ get_project_tools: z.strictObject({ repo: repoId }),
83
+ get_flows: z.strictObject({ repo: repoId.optional(), flow: id.optional(), card: id.optional() }),
84
+ // The flow contract's own schema (src/contracts/flow-propose.ts), never a second one here.
85
+ propose_flow: proposeFlowInputSchema,
86
+ get_teammates: z.strictObject({ repo: repoId.optional(), teammate: id.optional() }),
87
+ propose_teammate: z.strictObject({
88
+ operation: z.enum(["create", "edit_section", "edit_soul", "pause", "resume", "remove", "note", "answer", "use_tool", "stop_tool", "tool_rule", "forget", "edit_memory", "add_routine", "stop_routine", "undo"]),
89
+ section: text("teammateSection").optional(), schedule: text("teammateSchedule").optional(), routine: id.optional(), memory: id.optional(),
90
+ tool: text("teammateTool").optional(), action: text("teammateAction").optional(), undoWith: text("teammateAction").optional(), call: id.optional(),
91
+ use: z.enum(["free", "ask", "never"]).optional(), limitField: text("teammateAction").optional(), limitOver: z.number().min(0).optional(),
92
+ repo: repoId.optional(), teammate: id.optional(), template: enumOf(TEAMMATE_TEMPLATES.map(one => one.id)).optional(),
93
+ name: text("teammateName").optional(), soul: text("teammateSoul").optional(), note: text("teammateNote").optional(),
94
+ question: id.optional(), choice: text("teammateChoice").optional(), text: text("teammateText").optional(),
95
+ }),
96
+ get_flow_insights: z.strictObject({ repo: repoId.optional(), flow: id.optional(), days: z.int().min(1).max(90).optional(), card: id.optional(), entry: id.optional() }),
97
+ get_skills: z.strictObject({ repo: repoId, version: z.string().regex(/^[a-f0-9]{20}$/).optional(), offset: offset.optional() }),
98
+ get_project_knowledge: z.strictObject({ repo: repoId, reference: text("knowledgeReference").optional(), decision: id.optional() }),
99
+ get_task_conversation: z.strictObject({ task: taskRef, limit: z.int().min(1).max(30).optional() }),
100
+ commit_to: z.strictObject({
101
+ what: text("promise").min(3), when: z.enum(["task", "run", "check", "time"]), task: taskRef.optional(), run: id.optional(),
102
+ states: z.array(enumOf(TASK_STATES)).min(1).max(6).optional(), outcome: z.enum(RUN_OUTCOMES).optional(),
103
+ result: z.enum(CHECK_RESULTS).optional(), at: z.string().optional(), checkAfter: z.string().optional(),
104
+ }),
105
+ release_commitment: z.strictObject({ commitment: id, reason: text("promise").min(3) }),
106
+ remember: z.strictObject({
107
+ repo: repoId.optional(), kind: z.enum(["decision", "instruction", "about-you"]), text: text("rememberText").min(3), why: text("rememberWhy").optional(),
108
+ source: text("rememberSource").optional(), revision: offset.optional(), replaces: z.int().min(1).max(20).optional(),
109
+ }),
110
+ get_person: z.strictObject({ id: z.string().regex(PERSON_ID).optional(), name: text("personName").min(1).optional() }),
111
+ get_integrations: z.strictObject({}),
112
+ get_capabilities: z.strictObject({ repo: repoId.optional() }),
113
+ ask_owner: z.strictObject({ question: text("ownerQuestion").min(3), options: z.array(text("ownerOption").min(1)).min(2).max(4) }),
114
+ search_project_memory: z.strictObject({ query: text("memorySearch").min(2), repo: repoId.optional() }),
115
+ get_models: z.strictObject({}),
116
+ list_tasks: z.strictObject({ repo: repoId.optional(), state: z.enum(LISTED_TASK_STATES).optional(), limit: z.int().min(1).max(50).optional(), search: text("taskSearch").optional() }),
117
+ get_task: z.strictObject({ task: taskRef }),
118
+ get_agents: z.strictObject({ task: taskRef }),
119
+ list_decisions: z.strictObject({}),
120
+ get_decision: z.strictObject({ decision: id }),
121
+ queue: z.strictObject({ repo: repoId }),
122
+ propose_task: z.strictObject({
123
+ repo: repoId, title: text("taskTitle"), goal: scopeText,
124
+ // Exclusions left as null read as none, as they always have.
125
+ not: scopeText.nullable().optional(), touches: touches.optional(), acceptance,
126
+ planning: z.enum(["auto", "required", "skip"]).optional(), report: z.boolean().optional(), checks: text("checksWords").optional(),
127
+ }),
128
+ propose_next: z.strictObject({ task: taskRef }),
129
+ propose_reserve: z.strictObject({ task: taskRef, worker: text("workerName").nullable() }),
130
+ propose_hold: z.strictObject({ task: taskRef, reason: text("proposalReason") }),
131
+ propose_unhold: z.strictObject({ task: taskRef }),
132
+ propose_steer: z.strictObject({ task: taskRef, note }),
133
+ propose_dependency_repair: z.strictObject({ task: taskRef, blocker: taskRef, operation: z.enum(["retry", "unlink", "replace"]), replacement: taskRef.optional() }),
134
+ propose_scope: z.strictObject({ task: taskRef, goal: scopeText, not: scopeText.nullable().optional(), touches: touches.optional(), acceptance }),
135
+ propose_agents: z.strictObject({
136
+ task: taskRef, risk: z.enum(RISK_LEVELS).optional(),
137
+ size: z.enum(TASK_SIZES).optional(), risky: z.boolean().optional(),
138
+ role: z.enum(["planner", "builder", "repair"]).optional(),
139
+ agent: z.strictObject({ provider: text("agentProvider"), model: text("agentModel") }).optional(),
140
+ clear: z.boolean().optional(), why: text("agentWhy").optional(),
141
+ }),
142
+ propose_answer: z.strictObject({ decision: id, option: text("answerOption").min(1), rationale: text("answerRationale") }),
143
+ propose_cancel: z.strictObject({ task: taskRef, reason: text("proposalReason") }),
144
+ };
145
+ /**
146
+ * Read the same calls the 0.9.36 handlers accepted. Derive readers from the advertised contracts, leaving these
147
+ * optional values to their existing clamp/fallback code. Unknown top-level keys are stripped by the tool registry,
148
+ * except for flow proposals, whose strict contract predates item 5. The gateway also keeps its strict schemas.
149
+ */
150
+ export const LEAD_TOOL_READ_INPUTS = {
151
+ ...LEAD_TOOL_INPUTS,
152
+ list_tasks: LEAD_TOOL_INPUTS.list_tasks.extend({ limit: z.unknown().optional() }),
153
+ get_task_conversation: LEAD_TOOL_INPUTS.get_task_conversation.extend({ limit: z.unknown().optional() }),
154
+ // 0.9.36 read a null repo as every project.
155
+ get_flow_insights: LEAD_TOOL_INPUTS.get_flow_insights.extend({ repo: repoId.nullable().optional(), days: z.unknown().optional() }),
156
+ get_person: LEAD_TOOL_INPUTS.get_person.extend({ id: z.unknown().optional(), name: z.unknown().optional() }),
157
+ propose_agents: LEAD_TOOL_INPUTS.propose_agents.extend({ agent: LEAD_TOOL_INPUTS.propose_agents.shape.agent.unwrap().strip().optional() }),
158
+ };
159
+ /** How a tool's call names a key it doesn't know: propose_flow suggests the flow vocabulary's own words. */
160
+ export const LEAD_TOOL_OPTIONS = { propose_flow: FLOW_ALIASES };
161
+ // ------------------------------------------------------------- outputs
162
+ const str = z.string();
163
+ const int = z.number().int();
164
+ const list = z.array(z.unknown());
165
+ const nullable = (schema) => schema.nullable();
166
+ const rows = (shape) => z.array(z.looseObject(shape));
167
+ /** A proposal drafted as a card the operator confirms (propose_action, propose_flow, propose_teammate). */
168
+ const cardDrafted = z.looseObject({ proposal: int, label: str, awaiting: str, executed: z.literal(false) });
169
+ /** A proposal row drafted by one of the queue, hold, scope and answer tools. */
170
+ const proposed = (kind, shape = {}) => z.looseObject({ proposal: int, kind: z.literal(kind), awaiting: str, ...shape });
171
+ /** A repository-context read (repository-context.ts `RepositoryContext`). */
172
+ export const repositoryContextOutput = z.looseObject({ version: z.literal(1), readOnly: z.boolean(), query: str, mode: z.enum(["search", "impact"]), asOf: str, excerpts: list });
173
+ /** The local catch-up (assignment-brief.ts `AssignmentCatchUp`). */
174
+ export const catchUpOutput = z.looseObject({ version: z.literal(1), asOf: str, readOnly: z.literal(true), assignments: list, projects: list, omissions: z.looseObject({}) });
175
+ /** How things stand per repository (mate-tools.ts `recapOver`), its repos labelled. */
176
+ export const recapOutput = z.looseObject({ since: nullable(str), repos: list, waitsOnYou: z.looseObject({ decisions: list, incidents: int, scopesAwaitingApproval: list }), running: list, truncated: z.boolean() });
177
+ /** Open decisions (`decisionsOver`). */
178
+ export const decisionsOutput = z.looseObject({ decisions: rows({ decision: int, task: str, question: str, options: list, ageHours: z.number() }), truncated: z.boolean() });
179
+ /** One decision in full (`decisionOver`), without the builder's recommendation. */
180
+ export const decisionOutput = z.looseObject({ repo: str, decision: int, task: str, state: str, question: str, options: rows({ id: str, label: str, reversible: z.boolean(), consequence: str }), ageHours: z.number() });
181
+ /** One repository's queue by column (`queueOver`). */
182
+ export const queueColumnsOutput = { columns: rows({ column: str, tasks: list }) };
183
+ export const LEAD_TOOL_OUTPUTS = {
184
+ get_brief: catchUpOutput,
185
+ get_project_context: repositoryContextOutput,
186
+ get_action_status: z.looseObject({ proposal: int, operation: str, state: str, outcome: z.unknown(), finishedAt: nullable(str) }),
187
+ get_actions: z.looseObject({ actions: rows({ operation: str, label: str, secureReview: z.boolean(), inputs: z.array(str) }), notice: str }),
188
+ propose_action: cardDrafted,
189
+ propose_task_action: z.looseObject({ proposal: int, action: str, awaiting: str }),
190
+ get_controls: z.looseObject({ confirmedInChat: z.array(str), existingControls: rows({ id: str, label: str, needsTask: z.boolean(), needsProject: z.boolean() }), rule: str }),
191
+ show_control: z.looseObject({ card: int, label: str, action: str }),
192
+ get_result: z.looseObject({
193
+ task: str, root: str, currentExecution: str, run: int, title: str, feedback: list, feedbackTotal: int, nextFeedbackOffset: nullable(int),
194
+ changes: str, changesShortened: z.boolean(), verification: str, accepted: z.boolean(), evidenceTool: str, canRevise: z.boolean(),
195
+ }),
196
+ get_diff: z.looseObject({ task: str, run: int, files: rows({ path: str, added: int, removed: int }).optional(), file: str.optional(), diff: str.optional(), nextOffset: nullable(int).optional(), notice: nullable(str) }),
197
+ get_check_log: z.looseObject({ task: str, run: int, log: str, notice: nullable(str) }),
198
+ get_acceptance_evidence: z.looseObject({ task: str, run: int }),
199
+ get_result_images: z.looseObject({
200
+ task: str, run: int, imageCount: int, images: rows({ id: int, position: int, selected: z.boolean() }), selected: z.array(int), selectedCount: int,
201
+ sendCount: int, nextImageOffset: nullable(int), nextImageIds: z.array(int), unavailable: list, delivery: str,
202
+ }),
203
+ propose_review: proposed("review", { operation: z.enum(["note", "revise"]) }),
204
+ recap: recapOutput,
205
+ list_repos: z.looseObject({ repos: rows({ repo: str }) }),
206
+ get_project_tools: z.looseObject({ rule: str, tools: rows({ name: str }), connectBySigningIn: list, commonTools: list, foundOnThisComputer: list, notice: str }),
207
+ get_flows: z.union([
208
+ z.looseObject({ flow: int, name: str, steps: rows({ id: str, title: str, kind: str }), cards: rows({ card: int, title: str }), scripts: list, triggers: list, rule: str }),
209
+ z.looseObject({ flows: rows({ flow: int, name: str, cards: int, needYou: int, triggers: int }), templates: list, scripts: list, rule: str }),
210
+ ]),
211
+ propose_flow: cardDrafted,
212
+ get_teammates: z.looseObject({ teammates: rows({ teammate: int, name: str, working: z.boolean(), soul: str }), templates: list, rule: str }),
213
+ propose_teammate: cardDrafted,
214
+ get_flow_insights: z.union([
215
+ z.looseObject({ days: int, flows: rows({ flow: int, name: str }) }),
216
+ z.looseObject({ card: int, entry: int, state: str, log: str }),
217
+ z.looseObject({ runs: list, rule: str }),
218
+ ]),
219
+ get_skills: z.union([
220
+ z.looseObject({ version: str, instructions: str, nextOffset: nullable(int), notice: str }),
221
+ z.looseObject({ skills: list, nextOffset: nullable(int) }),
222
+ ]),
223
+ get_project_knowledge: z.looseObject({ revision: int }),
224
+ get_task_conversation: z.looseObject({ task: str, messages: rows({ from: z.enum(["operator", "lead"]), text: str, at: str }), notice: nullable(str) }),
225
+ commit_to: z.looseObject({ commitment: int, condition: str, expires: str, report: str }),
226
+ release_commitment: z.looseObject({ commitment: int, state: z.literal("cancelled") }),
227
+ remember: z.looseObject({ proposal: int, label: str, awaiting: str, executed: z.literal(false), next: str }),
228
+ get_person: z.union([z.looseObject({ person: z.looseObject({}) }), z.looseObject({ several: list, next: str })]),
229
+ get_integrations: z.looseObject({ integrations: rows({ name: str, kind: str, state: str }), settings: str }),
230
+ get_capabilities: z.looseObject({ projects: rows({ repo: str }), workers: z.unknown(), integrations: z.unknown(), rule: str }),
231
+ ask_owner: z.looseObject({ asked: str, options: z.array(str), shown: str }),
232
+ search_project_memory: z.looseObject({ hits: list, notice: str }),
233
+ get_models: z.looseObject({ roles: rows({ role: str }), tools: list, newModels: list, change: str }),
234
+ list_tasks: z.looseObject({ tasks: rows({ repo: str, task: str, execution: str, title: str, state: str, ageHours: z.number(), strikes: int }), truncated: z.boolean() }),
235
+ get_task: z.looseObject({
236
+ repo: str, task: str, root: str, currentExecution: str, title: str, state: str, deliverable: str, scope: str,
237
+ queue: nullable(z.looseObject({ position: int, of: int, column: str })), holds: list, attempts: int, decisionsOpen: int, dependencies: list,
238
+ }),
239
+ get_agents: z.looseObject({ repo: str, task: str, risk: z.looseObject({ level: str }), standing: str, agents: list, choices: z.looseObject({}), approval: str, organisationPolicy: z.looseObject({}) }),
240
+ list_decisions: decisionsOutput,
241
+ get_decision: decisionOutput,
242
+ queue: z.looseObject({ repo: str, queueRevision: int, ...queueColumnsOutput }),
243
+ propose_task: proposed("task", { repo: str, deliverable: z.enum(["branch", "report"]), planning: str }),
244
+ propose_next: proposed("next", { task: str }),
245
+ propose_reserve: proposed("reserve", { task: str, worker: nullable(str) }),
246
+ propose_hold: proposed("hold", { task: str }),
247
+ propose_unhold: proposed("unhold", { task: str }),
248
+ propose_steer: proposed("steer", { task: str }),
249
+ propose_dependency_repair: proposed("repair", { task: str, blocker: str, operation: z.enum(["retry", "unlink", "replace"]) }),
250
+ propose_scope: proposed("scope", { task: str }),
251
+ propose_agents: proposed("agents", { task: str }),
252
+ propose_answer: proposed("answer", { decision: int, option: str }),
253
+ propose_cancel: proposed("cancel", { task: str }),
254
+ };
255
+ /**
256
+ * Report a result that disagrees with its output schema without hiding the actual result, including under Vitest.
257
+ * Contract tests assert output shapes directly; executing a tool always writes the diagnostic and returns its result.
258
+ */
259
+ export function reportToolOutput(surface, tool, schema, body) {
260
+ const read = parseContract(schema, body);
261
+ if (read.ok)
262
+ return;
263
+ const line = `${surface} tool ${tool}: its result disagrees with its output schema — ${read.issues.map(one => one.line).join("; ")}`;
264
+ process.stderr.write(`${line}\n`);
265
+ }
@@ -0,0 +1,158 @@
1
+ /**
2
+ * The memory pass (memory-pass.ts): one schema for the verdict a model returns on one session — the analyser's
3
+ * `--json-schema` (Codex's output schema), with a tolerant read side for older answers — the verdict each session keeps
4
+ * (`memory_session.verdict`), and a proposal with its evidence (`memory_proposal`). What JSON Schema cannot state —
5
+ * a quote really in the trace, an instruction id on the surface audited — is checked in plain code after parsing.
6
+ */
7
+ import { z } from "zod";
8
+ import { type ContractResult } from "./contract.js";
9
+ export declare const MEMORY_EFFECTS: readonly ["harm", "non-compliance", "irrelevant"];
10
+ export declare const MEMORY_DOMAINS: readonly ["project", "orchestration"];
11
+ export declare const MEMORY_PROPOSAL_KINDS: readonly ["instruction-add", "instruction-remove", "decision-add"];
12
+ /** The verdict a model returns on one session. */
13
+ export declare const memoryVerdictSchema: z.ZodObject<{
14
+ positive: z.ZodArray<z.ZodObject<{
15
+ instruction: z.ZodString;
16
+ effect: z.ZodString;
17
+ quote: z.ZodString;
18
+ }, z.core.$strict>>;
19
+ negative: z.ZodArray<z.ZodObject<{
20
+ instruction: z.ZodString;
21
+ effect: z.ZodString;
22
+ class: z.ZodEnum<{
23
+ harm: "harm";
24
+ irrelevant: "irrelevant";
25
+ "non-compliance": "non-compliance";
26
+ }>;
27
+ quote: z.ZodString;
28
+ }, z.core.$strict>>;
29
+ gaps: z.ZodArray<z.ZodObject<{
30
+ mistake: z.ZodString;
31
+ proposedInstruction: z.ZodString;
32
+ domain: z.ZodEnum<{
33
+ orchestration: "orchestration";
34
+ project: "project";
35
+ }>;
36
+ quote: z.ZodString;
37
+ matchesGap: z.ZodNullable<z.ZodString>;
38
+ }, z.core.$strict>>;
39
+ }, z.core.$strict>;
40
+ /** What the analyser is told to return: `--json-schema` for Claude, the output schema for Codex, and the prompt for
41
+ * every other provider. */
42
+ export declare const MEMORY_VERDICT_MODEL_SCHEMA: Record<string, unknown>;
43
+ export type Verdict = z.infer<typeof memoryVerdictSchema>;
44
+ export declare const memoryVerdictReadSchema: z.ZodObject<{
45
+ positive: z.ZodCatch<z.ZodArray<z.ZodCatch<z.ZodNullable<z.ZodPipe<z.ZodObject<{
46
+ quote: z.ZodString;
47
+ }, z.core.$loose>, z.ZodTransform<{
48
+ quote: string;
49
+ claim: {
50
+ quote: string;
51
+ instruction: string;
52
+ effect: string;
53
+ } | null;
54
+ }, {
55
+ [x: string]: unknown;
56
+ quote: string;
57
+ }>>>>>>;
58
+ negative: z.ZodCatch<z.ZodArray<z.ZodCatch<z.ZodNullable<z.ZodPipe<z.ZodObject<{
59
+ quote: z.ZodString;
60
+ }, z.core.$loose>, z.ZodTransform<{
61
+ quote: string;
62
+ claim: {
63
+ quote: string;
64
+ instruction: string;
65
+ effect: string;
66
+ class: "harm" | "irrelevant" | "non-compliance";
67
+ } | null;
68
+ }, {
69
+ [x: string]: unknown;
70
+ quote: string;
71
+ }>>>>>>;
72
+ gaps: z.ZodCatch<z.ZodArray<z.ZodCatch<z.ZodNullable<z.ZodPipe<z.ZodObject<{
73
+ quote: z.ZodString;
74
+ }, z.core.$loose>, z.ZodTransform<{
75
+ quote: string;
76
+ claim: {
77
+ quote: string;
78
+ mistake: string;
79
+ proposedInstruction: string;
80
+ domain: "orchestration" | "project";
81
+ matchesGap: string | null;
82
+ } | null;
83
+ }, {
84
+ [x: string]: unknown;
85
+ quote: string;
86
+ }>>>>>>;
87
+ }, z.core.$strip>;
88
+ export declare const MEMORY_VERDICT_VERSION = 1;
89
+ /** A verdict as a session keeps it. */
90
+ export declare const savedVerdictSchema: z.ZodObject<{
91
+ version: z.ZodLiteral<1>;
92
+ positive: z.ZodArray<z.ZodObject<{
93
+ instruction: z.ZodString;
94
+ effect: z.ZodString;
95
+ quote: z.ZodString;
96
+ }, z.core.$strict>>;
97
+ negative: z.ZodArray<z.ZodObject<{
98
+ instruction: z.ZodString;
99
+ effect: z.ZodString;
100
+ class: z.ZodEnum<{
101
+ harm: "harm";
102
+ irrelevant: "irrelevant";
103
+ "non-compliance": "non-compliance";
104
+ }>;
105
+ quote: z.ZodString;
106
+ }, z.core.$strict>>;
107
+ gaps: z.ZodArray<z.ZodObject<{
108
+ mistake: z.ZodString;
109
+ proposedInstruction: z.ZodString;
110
+ domain: z.ZodEnum<{
111
+ orchestration: "orchestration";
112
+ project: "project";
113
+ }>;
114
+ quote: z.ZodString;
115
+ matchesGap: z.ZodNullable<z.ZodString>;
116
+ }, z.core.$strict>>;
117
+ }, z.core.$strict>;
118
+ export type SavedVerdict = z.infer<typeof savedVerdictSchema>;
119
+ /**
120
+ * A verdict kept before it carried `version` (through 0.9.36), as version 1. Those left a gap's `matchesGap` out when
121
+ * there was none; that reads as null. Values are never rewritten.
122
+ */
123
+ export declare function upgradeVerdictV0(body: Record<string, unknown>): Record<string, unknown>;
124
+ export declare const MEMORY_VERDICT_UPGRADES: {
125
+ readonly 0: typeof upgradeVerdictV0;
126
+ };
127
+ /** Read a kept verdict: version 1 as itself, an unversioned one upgraded, a newer one refused plainly. */
128
+ export declare function readSavedVerdict(input: unknown): ContractResult<SavedVerdict>;
129
+ /** One corroborating sighting: the session and its verbatim quote. A proposal keeps at most six. */
130
+ export declare const memoryEvidenceSchema: z.ZodArray<z.ZodObject<{
131
+ session: z.ZodString;
132
+ quote: z.ZodString;
133
+ }, z.core.$strict>>;
134
+ export type MemoryEvidence = z.infer<typeof memoryEvidenceSchema>;
135
+ /** A proposal as its row reads; its evidence is a JSON list (an older runtime reads it as one, so it has no envelope). */
136
+ export declare const memoryProposalSchema: z.ZodObject<{
137
+ id: z.ZodInt;
138
+ repo: z.ZodString;
139
+ kind: z.ZodEnum<{
140
+ "decision-add": "decision-add";
141
+ "instruction-add": "instruction-add";
142
+ "instruction-remove": "instruction-remove";
143
+ }>;
144
+ fingerprint: z.ZodString;
145
+ title: z.ZodString;
146
+ rationale: z.ZodString;
147
+ beforeText: z.ZodNullable<z.ZodString>;
148
+ afterText: z.ZodString;
149
+ evidence: z.ZodArray<z.ZodObject<{
150
+ session: z.ZodString;
151
+ quote: z.ZodString;
152
+ }, z.core.$strict>>;
153
+ sessions: z.ZodInt;
154
+ status: z.ZodString;
155
+ createdAt: z.ZodString;
156
+ surface: z.ZodString;
157
+ }, z.core.$strict>;
158
+ export type MemoryProposal = z.infer<typeof memoryProposalSchema>;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * The memory pass (memory-pass.ts): one schema for the verdict a model returns on one session — the analyser's
3
+ * `--json-schema` (Codex's output schema), with a tolerant read side for older answers — the verdict each session keeps
4
+ * (`memory_session.verdict`), and a proposal with its evidence (`memory_proposal`). What JSON Schema cannot state —
5
+ * a quote really in the trace, an instruction id on the surface audited — is checked in plain code after parsing.
6
+ */
7
+ import { z } from "zod";
8
+ import { TEXT_LIMITS } from "../text-limits.js";
9
+ import { limited, readVersioned, toModelSchema, versioned } from "./contract.js";
10
+ export const MEMORY_EFFECTS = ["harm", "non-compliance", "irrelevant"];
11
+ export const MEMORY_DOMAINS = ["project", "orchestration"];
12
+ export const MEMORY_PROPOSAL_KINDS = ["instruction-add", "instruction-remove", "decision-add"];
13
+ const bounded = limited;
14
+ const saved = () => z.string();
15
+ const verdictShape = (text) => {
16
+ const instruction = z.string().describe("An instruction's id from INSTRUCTIONS, such as IN-001.");
17
+ const quote = text("quote", "memoryTrace").describe("Copied verbatim from the trace, at least 12 characters.");
18
+ return {
19
+ positive: z.array(z.strictObject({ instruction, effect: text("effect", "memoryEffect"), quote })),
20
+ negative: z.array(z.strictObject({ instruction, effect: text("effect", "memoryEffect"), class: z.enum(MEMORY_EFFECTS), quote })),
21
+ gaps: z.array(z.strictObject({
22
+ mistake: text("mistake", "memoryMistake"),
23
+ proposedInstruction: text("proposed instruction (one imperative sentence)", "memoryInstruction"),
24
+ domain: z.enum(MEMORY_DOMAINS),
25
+ quote,
26
+ matchesGap: z.string().nullable().describe("The key of a gap already on the books when it is the same underlying gap, otherwise null."),
27
+ })),
28
+ };
29
+ };
30
+ /** The verdict a model returns on one session. */
31
+ export const memoryVerdictSchema = z.strictObject(verdictShape(bounded));
32
+ /** What the analyser is told to return: `--json-schema` for Claude, the output schema for Codex, and the prompt for
33
+ * every other provider. */
34
+ export const MEMORY_VERDICT_MODEL_SCHEMA = toModelSchema(memoryVerdictSchema);
35
+ /** Parse-side compatibility with the pre-Zod analyser: clip with the same trailing ellipsis, coerce effects and
36
+ * instruction ids, default gap metadata, ignore unknown keys and discard bad claims individually. These transforms
37
+ * never enter the strict model schema. */
38
+ const clipped = (key) => z.string().transform(text => text.length > TEXT_LIMITS[key] ? `${text.slice(0, TEXT_LIMITS[key])}…` : text);
39
+ const readShape = verdictShape(saved);
40
+ const effectFields = {
41
+ instruction: z.coerce.string(),
42
+ effect: z.preprocess(value => String(value ?? ""), clipped("memoryEffect")),
43
+ };
44
+ /** Keep the quote even when a claim is bad: the old reader applied its twenty-quote cap before dropping bad claims. */
45
+ function readClaims(schema) {
46
+ return z.array(z.looseObject({ quote: z.string() }).transform(input => {
47
+ const parsed = schema.safeParse(input);
48
+ return { quote: input.quote, claim: parsed.success ? parsed.data : null };
49
+ }).nullable().catch(null)).catch([]);
50
+ }
51
+ export const memoryVerdictReadSchema = memoryVerdictSchema.strip().extend({
52
+ positive: readClaims(readShape.positive.element.strip().extend(effectFields)),
53
+ negative: readClaims(readShape.negative.element.strip().extend({
54
+ ...effectFields,
55
+ class: z.coerce.string().pipe(readShape.negative.element.shape.class),
56
+ })),
57
+ gaps: readClaims(readShape.gaps.element.strip().extend({
58
+ mistake: clipped("memoryMistake"),
59
+ proposedInstruction: clipped("memoryInstruction"),
60
+ domain: z.preprocess(value => value === "orchestration" ? value : "project", readShape.gaps.element.shape.domain),
61
+ matchesGap: readShape.gaps.element.shape.matchesGap.catch(null),
62
+ })),
63
+ });
64
+ export const MEMORY_VERDICT_VERSION = 1;
65
+ /** A verdict as a session keeps it. */
66
+ export const savedVerdictSchema = versioned(MEMORY_VERDICT_VERSION, verdictShape(saved));
67
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
68
+ /**
69
+ * A verdict kept before it carried `version` (through 0.9.36), as version 1. Those left a gap's `matchesGap` out when
70
+ * there was none; that reads as null. Values are never rewritten.
71
+ */
72
+ export function upgradeVerdictV0(body) {
73
+ const gaps = body["gaps"];
74
+ return {
75
+ version: MEMORY_VERDICT_VERSION,
76
+ ...body,
77
+ ...(Array.isArray(gaps) ? { gaps: gaps.map(gap => (isRecord(gap) && !Object.prototype.hasOwnProperty.call(gap, "matchesGap") ? { ...gap, matchesGap: null } : gap)) } : {}),
78
+ };
79
+ }
80
+ export const MEMORY_VERDICT_UPGRADES = { 0: upgradeVerdictV0 };
81
+ /** Read a kept verdict: version 1 as itself, an unversioned one upgraded, a newer one refused plainly. */
82
+ export function readSavedVerdict(input) {
83
+ return readVersioned(savedVerdictSchema, input, MEMORY_VERDICT_UPGRADES);
84
+ }
85
+ /** One corroborating sighting: the session and its verbatim quote. A proposal keeps at most six. */
86
+ export const memoryEvidenceSchema = z.array(z.strictObject({ session: z.string(), quote: z.string() })).max(6);
87
+ /** A proposal as its row reads; its evidence is a JSON list (an older runtime reads it as one, so it has no envelope). */
88
+ export const memoryProposalSchema = z.strictObject({
89
+ id: z.int(),
90
+ repo: z.string(),
91
+ kind: z.enum(MEMORY_PROPOSAL_KINDS),
92
+ fingerprint: z.string(),
93
+ title: z.string(),
94
+ rationale: z.string(),
95
+ beforeText: z.string().nullable(),
96
+ afterText: z.string(),
97
+ evidence: memoryEvidenceSchema,
98
+ sessions: z.int(),
99
+ status: z.string(),
100
+ createdAt: z.string(),
101
+ surface: z.string(),
102
+ });
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Project knowledge (project-knowledge.ts): one schema for the saved knowledge (instructions and references, every
3
+ * revision in `knowledge_change` and the current one in `project_knowledge`), and for the selection a run is given and
4
+ * keeps (`knowledge_snapshot`). The rules JSON Schema cannot state — UTF-8 byte limits, control characters, secrets,
5
+ * the selection's total size — run in plain code when knowledge is written. A saved payload's digest is checked
6
+ * against its stored bytes before it is parsed, and it is upgraded only in memory: stored bytes are never rewritten.
7
+ */
8
+ import { z } from "zod";
9
+ import type { RepositoryContext } from "../repository-context.js";
10
+ import { type ContractResult } from "./contract.js";
11
+ /** How many references a project keeps, and how many a run is given. Text limits live in TEXT_LIMITS. */
12
+ export declare const KNOWLEDGE_COUNTS: {
13
+ readonly references: 12;
14
+ readonly selected: 3;
15
+ readonly decisions: 8;
16
+ };
17
+ export declare const KNOWLEDGE_VERSION = 1;
18
+ /** Knowledge as it is written. */
19
+ export declare const knowledgeSchema: z.ZodObject<{
20
+ version: z.ZodLiteral<1>;
21
+ instructions: z.ZodString;
22
+ references: z.ZodArray<z.ZodObject<{
23
+ id: z.ZodString;
24
+ title: z.ZodString;
25
+ content: z.ZodString;
26
+ path: z.ZodNullable<z.ZodString>;
27
+ sourceSha: z.ZodNullable<z.ZodString>;
28
+ sourceRevision: z.ZodNullable<z.ZodString>;
29
+ }, z.core.$strict>>;
30
+ }, z.core.$strict>;
31
+ /** Knowledge as it is read back. */
32
+ export declare const savedKnowledgeSchema: z.ZodObject<{
33
+ version: z.ZodLiteral<1>;
34
+ instructions: z.ZodString;
35
+ references: z.ZodArray<z.ZodObject<{
36
+ id: z.ZodString;
37
+ title: z.ZodString;
38
+ content: z.ZodString;
39
+ path: z.ZodNullable<z.ZodString>;
40
+ sourceSha: z.ZodNullable<z.ZodString>;
41
+ sourceRevision: z.ZodNullable<z.ZodString>;
42
+ }, z.core.$strict>>;
43
+ }, z.core.$strict>;
44
+ /** Knowledge as every reader returns it: the payload without its envelope. */
45
+ export type Knowledge = Omit<z.infer<typeof knowledgeSchema>, "version">;
46
+ export type KnowledgeReference = Knowledge["references"][number];
47
+ /**
48
+ * Knowledge saved before it carried `version` (through 0.9.36: `{instructions, references}`), as version 1. Its
49
+ * readers took the fields they knew, so this keeps exactly those; values are never rewritten.
50
+ */
51
+ export declare function upgradeKnowledgeV0(body: Record<string, unknown>): Record<string, unknown>;
52
+ export declare const KNOWLEDGE_UPGRADES: {
53
+ readonly 0: typeof upgradeKnowledgeV0;
54
+ };
55
+ /** Read saved knowledge: version 1 as itself, an unversioned one upgraded, a newer one refused plainly. */
56
+ export declare function readKnowledge(input: unknown): ContractResult<Knowledge>;
57
+ /** What a person drafts on the Knowledge page or the CLI; each action reads the fields it needs. */
58
+ export declare const knowledgeDraftSchema: z.ZodObject<{
59
+ instructions: z.ZodOptional<z.ZodString>;
60
+ title: z.ZodOptional<z.ZodString>;
61
+ content: z.ZodOptional<z.ZodString>;
62
+ path: z.ZodOptional<z.ZodString>;
63
+ id: z.ZodOptional<z.ZodString>;
64
+ }, z.core.$strict>;
65
+ export type KnowledgeDraft = z.infer<typeof knowledgeDraftSchema>;
66
+ /** A source a run was not given, and why. */
67
+ export declare const knowledgeOmissionSchema: z.ZodObject<{
68
+ title: z.ZodString;
69
+ reason: z.ZodString;
70
+ }, z.core.$strict>;
71
+ export declare const KNOWLEDGE_SELECTION_VERSION = 1;
72
+ /** The selection a run is given, as it is frozen. The keys are in the order a selection has always been written. */
73
+ export declare const knowledgeSelectionSchema: z.ZodObject<{
74
+ version: z.ZodLiteral<1>;
75
+ revision: z.ZodInt;
76
+ instructions: z.ZodString;
77
+ references: z.ZodArray<z.ZodObject<{
78
+ id: z.ZodString;
79
+ title: z.ZodString;
80
+ content: z.ZodString;
81
+ path: z.ZodNullable<z.ZodString>;
82
+ sourceSha: z.ZodNullable<z.ZodString>;
83
+ sourceRevision: z.ZodNullable<z.ZodString>;
84
+ }, z.core.$strict>>;
85
+ omitted: z.ZodArray<z.ZodObject<{
86
+ title: z.ZodString;
87
+ reason: z.ZodString;
88
+ }, z.core.$strict>>;
89
+ inheritedFrom: z.ZodNullable<z.ZodInt>;
90
+ decisions: z.ZodOptional<z.ZodArray<z.ZodObject<{
91
+ id: z.ZodInt;
92
+ claim: z.ZodString;
93
+ decidedAt: z.ZodString;
94
+ }, z.core.$strict>>>;
95
+ repository: z.ZodOptional<z.ZodObject<{}, z.core.$loose>>;
96
+ }, z.core.$strict>;
97
+ /** A frozen selection as it is read back. */
98
+ export declare const savedKnowledgeSelectionSchema: z.ZodObject<{
99
+ version: z.ZodLiteral<1>;
100
+ revision: z.ZodInt;
101
+ instructions: z.ZodString;
102
+ references: z.ZodArray<z.ZodObject<{
103
+ id: z.ZodString;
104
+ title: z.ZodString;
105
+ content: z.ZodString;
106
+ path: z.ZodNullable<z.ZodString>;
107
+ sourceSha: z.ZodNullable<z.ZodString>;
108
+ sourceRevision: z.ZodNullable<z.ZodString>;
109
+ }, z.core.$strict>>;
110
+ omitted: z.ZodArray<z.ZodObject<{
111
+ title: z.ZodString;
112
+ reason: z.ZodString;
113
+ }, z.core.$strict>>;
114
+ inheritedFrom: z.ZodNullable<z.ZodInt>;
115
+ decisions: z.ZodOptional<z.ZodArray<z.ZodObject<{
116
+ id: z.ZodInt;
117
+ claim: z.ZodString;
118
+ decidedAt: z.ZodString;
119
+ }, z.core.$strict>>>;
120
+ repository: z.ZodOptional<z.ZodObject<{}, z.core.$loose>>;
121
+ }, z.core.$strict>;
122
+ export type KnowledgeSelection = Omit<z.infer<typeof knowledgeSelectionSchema>, "repository"> & {
123
+ repository?: RepositoryContext;
124
+ };
125
+ /** Read a frozen selection: version 1 (every selection ever frozen) as itself, a newer one refused plainly. */
126
+ export declare function readKnowledgeSelection(input: unknown): ContractResult<KnowledgeSelection>;