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,136 @@
1
+ /**
2
+ * The Slack, Discord and Teams receipt tables (chat-delivery-state.ts keeps their rows). A leaf: it imports nothing, so
3
+ * the store can create these tables without loading the chat state modules that themselves import the store.
4
+ */
5
+ const CHAT_SCHEMA = `
6
+ CREATE TABLE IF NOT EXISTS chat_binding (
7
+ id INTEGER PRIMARY KEY AUTOINCREMENT, installation TEXT NOT NULL,
8
+ team TEXT NOT NULL, app TEXT NOT NULL, member TEXT NOT NULL, channel TEXT NOT NULL,
9
+ approver TEXT NOT NULL, generation INTEGER NOT NULL, created TEXT NOT NULL, revoked TEXT
10
+ );
11
+ CREATE UNIQUE INDEX IF NOT EXISTS chat_one_binding_member ON chat_binding(installation, member) WHERE revoked IS NULL;
12
+ CREATE TABLE IF NOT EXISTS chat_pair (
13
+ hash TEXT PRIMARY KEY, installation TEXT NOT NULL, approver TEXT NOT NULL,
14
+ generation INTEGER NOT NULL, expires TEXT NOT NULL, consumed TEXT
15
+ );
16
+ CREATE TABLE IF NOT EXISTS chat_event (
17
+ id TEXT PRIMARY KEY, installation TEXT NOT NULL, binding INTEGER REFERENCES chat_binding(id),
18
+ kind TEXT NOT NULL CHECK(kind IN ('message','action','pair','notice')),
19
+ channel TEXT NOT NULL, member TEXT NOT NULL, ts TEXT NOT NULL, thread TEXT NOT NULL,
20
+ payload TEXT NOT NULL, created TEXT NOT NULL,
21
+ session INTEGER REFERENCES mate_session(id), state TEXT NOT NULL DEFAULT 'queued' CHECK(state IN ('queued','done','dropped')),
22
+ next_at TEXT, problem TEXT
23
+ );
24
+ CREATE INDEX IF NOT EXISTS chat_pending_event ON chat_event(installation,state,next_at);
25
+ CREATE TABLE IF NOT EXISTS chat_part (
26
+ id INTEGER PRIMARY KEY AUTOINCREMENT, event TEXT NOT NULL REFERENCES chat_event(id), ordinal INTEGER NOT NULL,
27
+ payload TEXT NOT NULL, state TEXT NOT NULL DEFAULT 'pending' CHECK(state IN ('pending','sent','dropped')),
28
+ message TEXT, file TEXT, uploaded INTEGER NOT NULL DEFAULT 0, attempts INTEGER NOT NULL DEFAULT 0,
29
+ uncertain INTEGER NOT NULL DEFAULT 0, next_at TEXT, problem TEXT, created TEXT NOT NULL,
30
+ UNIQUE(event,ordinal)
31
+ );
32
+ CREATE TABLE IF NOT EXISTS chat_action (
33
+ token TEXT PRIMARY KEY, part INTEGER NOT NULL REFERENCES chat_part(id),
34
+ proposal INTEGER NOT NULL REFERENCES mate_proposal(id), phase TEXT NOT NULL CHECK(phase IN ('confirm','dismiss','yes','cancel')),
35
+ expires TEXT NOT NULL, consumed TEXT
36
+ );
37
+ CREATE TABLE IF NOT EXISTS chat_progress (
38
+ binding INTEGER NOT NULL REFERENCES chat_binding(id), run INTEGER NOT NULL REFERENCES run(id),
39
+ part INTEGER NOT NULL REFERENCES chat_part(id), digest TEXT NOT NULL,
40
+ PRIMARY KEY(binding,run)
41
+ );
42
+ CREATE TABLE IF NOT EXISTS chat_runtime (
43
+ installation TEXT PRIMARY KEY, owner TEXT, lease_until TEXT, connected TEXT,
44
+ problem TEXT, retry_at TEXT, notification INTEGER NOT NULL DEFAULT 0
45
+ );
46
+ `;
47
+ /** v73: a chat's place in the shared team conversations — a channel follows one
48
+ * conversation, a DM may select one. Rows are revoked, never deleted. */
49
+ const CHAT_ROOM_SCHEMA = `
50
+ CREATE TABLE IF NOT EXISTS chat_room (
51
+ id INTEGER PRIMARY KEY AUTOINCREMENT, installation TEXT NOT NULL, chat TEXT NOT NULL,
52
+ kind TEXT NOT NULL CHECK(kind IN ('group','private')), conversation TEXT NOT NULL,
53
+ binding INTEGER NOT NULL REFERENCES chat_binding(id),
54
+ bound_by TEXT NOT NULL, bound TEXT NOT NULL, cursor INTEGER NOT NULL DEFAULT 0,
55
+ revoked TEXT, revoked_by TEXT
56
+ );
57
+ CREATE UNIQUE INDEX IF NOT EXISTS chat_room_live ON chat_room(installation, chat) WHERE revoked IS NULL;
58
+ CREATE UNIQUE INDEX IF NOT EXISTS chat_room_group ON chat_room(conversation) WHERE revoked IS NULL AND kind='group';
59
+ CREATE TABLE IF NOT EXISTS chat_meta (
60
+ installation TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL, updated TEXT NOT NULL,
61
+ PRIMARY KEY(installation, key)
62
+ );
63
+ `;
64
+ /** v88: flow decisions in the chat app, as on Telegram (telegram-flow.ts).
65
+ * A button is one opaque token for one visit (card, entry) of one card, on
66
+ * the part it rides; Edit and Send back open a prompt the person's next
67
+ * message in their DM answers. */
68
+ const CHAT_FLOW_SCHEMA = `
69
+ CREATE TABLE IF NOT EXISTS chat_flow_action (
70
+ token TEXT PRIMARY KEY, part INTEGER NOT NULL REFERENCES chat_part(id),
71
+ card INTEGER NOT NULL REFERENCES flow_card(id), entry INTEGER NOT NULL,
72
+ action TEXT NOT NULL CHECK(action IN ('approve','edit','send-back')), expires TEXT NOT NULL, consumed TEXT
73
+ );
74
+ CREATE INDEX IF NOT EXISTS chat_flow_action_visit ON chat_flow_action(card, entry);
75
+ CREATE TABLE IF NOT EXISTS chat_flow_prompt (
76
+ id INTEGER PRIMARY KEY AUTOINCREMENT, binding INTEGER NOT NULL REFERENCES chat_binding(id),
77
+ card INTEGER NOT NULL REFERENCES flow_card(id), entry INTEGER NOT NULL,
78
+ mode TEXT NOT NULL CHECK(mode IN ('edit','send-back')), created TEXT NOT NULL, expires TEXT NOT NULL, consumed TEXT
79
+ );
80
+ CREATE INDEX IF NOT EXISTS chat_flow_prompt_open ON chat_flow_prompt(binding, consumed);
81
+ `;
82
+ /** A flow's "Person chooses" zone in the chat app (flow-send.ts): one button per option (choice is its index, label its words
83
+ * when sent) for one visit of one card, on the part it rides. A reply in that notice's thread is the note; chat_flow_note
84
+ * is "Use this as your note?" Yes / No about one other message (held: its event; words: what it said, kept only until
85
+ * answered, as an event's own payload is cleared once it is handled), asked once per visit.
86
+ * Never in CHAT_TABLES: an older database has no such table until this schema creates it. */
87
+ const CHAT_FLOW_CHOICE_SCHEMA = `
88
+ CREATE TABLE IF NOT EXISTS chat_flow_choice (
89
+ token TEXT PRIMARY KEY, part INTEGER NOT NULL REFERENCES chat_part(id),
90
+ card INTEGER NOT NULL REFERENCES flow_card(id), entry INTEGER NOT NULL,
91
+ choice INTEGER NOT NULL, label TEXT NOT NULL, expires TEXT NOT NULL, consumed TEXT
92
+ );
93
+ CREATE INDEX IF NOT EXISTS chat_flow_choice_visit ON chat_flow_choice(card, entry);
94
+ CREATE TABLE IF NOT EXISTS chat_flow_note (
95
+ token TEXT PRIMARY KEY, part INTEGER NOT NULL REFERENCES chat_part(id),
96
+ card INTEGER NOT NULL REFERENCES flow_card(id), entry INTEGER NOT NULL,
97
+ held TEXT NOT NULL, words TEXT, answer TEXT NOT NULL CHECK(answer IN ('yes','no')), expires TEXT NOT NULL, consumed TEXT
98
+ );
99
+ CREATE INDEX IF NOT EXISTS chat_flow_note_visit ON chat_flow_note(card, entry);
100
+ `;
101
+ /** v93: a teammate's question in the chat app. One button per option (choice)
102
+ * and one to answer in words (choice NULL); that one opens a prompt the
103
+ * person's next message in their DM answers. Never in CHAT_TABLES: an older
104
+ * database has no such tables until this schema creates them. */
105
+ const CHAT_QUESTION_SCHEMA = `
106
+ CREATE TABLE IF NOT EXISTS chat_question_action (
107
+ token TEXT PRIMARY KEY, part INTEGER NOT NULL REFERENCES chat_part(id),
108
+ question INTEGER NOT NULL REFERENCES teammate_question(id), choice TEXT, expires TEXT NOT NULL, consumed TEXT
109
+ );
110
+ CREATE INDEX IF NOT EXISTS chat_question_action_question ON chat_question_action(question);
111
+ CREATE TABLE IF NOT EXISTS chat_question_prompt (
112
+ id INTEGER PRIMARY KEY AUTOINCREMENT, binding INTEGER NOT NULL REFERENCES chat_binding(id),
113
+ question INTEGER NOT NULL REFERENCES teammate_question(id), created TEXT NOT NULL, expires TEXT NOT NULL, consumed TEXT
114
+ );
115
+ CREATE INDEX IF NOT EXISTS chat_question_prompt_open ON chat_question_prompt(binding, consumed);
116
+ `;
117
+ /** The lead's question to its owner (ask_owner) in the chat app: one button per option (choice is its index) and one for
118
+ * "Something else" (choice NULL). A tap sends the option as the owner's next message. Never in CHAT_TABLES. */
119
+ const CHAT_ASK_SCHEMA = `
120
+ CREATE TABLE IF NOT EXISTS chat_ask_action (
121
+ token TEXT PRIMARY KEY, part INTEGER NOT NULL REFERENCES chat_part(id) ON DELETE CASCADE,
122
+ turn INTEGER NOT NULL, choice INTEGER, expires TEXT NOT NULL, consumed TEXT
123
+ );
124
+ CREATE INDEX IF NOT EXISTS chat_ask_action_turn ON chat_ask_action(turn);
125
+ `;
126
+ const CHAT_TABLES = [
127
+ "chat_binding",
128
+ "chat_pair",
129
+ "chat_event",
130
+ "chat_part",
131
+ "chat_action",
132
+ "chat_progress",
133
+ "chat_runtime",
134
+ ];
135
+ export const chatSchema = (channel) => (CHAT_SCHEMA + CHAT_ROOM_SCHEMA + CHAT_FLOW_SCHEMA + CHAT_FLOW_CHOICE_SCHEMA + CHAT_QUESTION_SCHEMA + CHAT_ASK_SCHEMA).replaceAll("chat_", `${channel}_`);
136
+ export const chatTables = (channel) => CHAT_TABLES.map((name) => name.replace("chat_", `${channel}_`));
@@ -0,0 +1,97 @@
1
+ /**
2
+ * The managed context a desktop coding session captures once (`prepareCodingContext` in coding-context.ts): the
3
+ * frozen text the agent is given, what it was selected from, and the digest that ties the two together. It is saved
4
+ * inside the session's workspace record and checked again (`verifyCodingContext`) before every resume.
5
+ *
6
+ * `metadata` keeps its own `version: 1` because it is part of the digest; its keys are listed in the order the digest
7
+ * was taken in, so a parsed capture digests exactly as it was saved. Repository identity, digest and the materialized
8
+ * skill files are checked in plain code after parsing.
9
+ */
10
+ import { z } from "zod";
11
+ import { type ContractResult } from "./contract.js";
12
+ export declare const codingContextMetadataSchema: z.ZodObject<{
13
+ version: z.ZodLiteral<1>;
14
+ repo: z.ZodString;
15
+ identity: z.ZodString;
16
+ baseRevision: z.ZodString;
17
+ knowledge: z.ZodObject<{
18
+ revision: z.ZodInt;
19
+ selectionSha256: z.ZodString;
20
+ references: z.ZodArray<z.ZodObject<{
21
+ id: z.ZodString;
22
+ title: z.ZodString;
23
+ path: z.ZodNullable<z.ZodString>;
24
+ sourceSha: z.ZodNullable<z.ZodString>;
25
+ sourceRevision: z.ZodNullable<z.ZodString>;
26
+ }, z.core.$strict>>;
27
+ omitted: z.ZodArray<z.ZodObject<{
28
+ title: z.ZodString;
29
+ reason: z.ZodString;
30
+ }, z.core.$strict>>;
31
+ }, z.core.$strict>;
32
+ skills: z.ZodObject<{
33
+ revision: z.ZodInt;
34
+ packages: z.ZodArray<z.ZodObject<{
35
+ name: z.ZodString;
36
+ sha256: z.ZodString;
37
+ skillFile: z.ZodString;
38
+ }, z.core.$strict>>;
39
+ }, z.core.$strict>;
40
+ directory: z.ZodNullable<z.ZodString>;
41
+ files: z.ZodArray<z.ZodObject<{
42
+ path: z.ZodString;
43
+ sha256: z.ZodString;
44
+ }, z.core.$strict>>;
45
+ }, z.core.$strict>;
46
+ export declare const CODING_CONTEXT_VERSION = 1;
47
+ export declare const codingContextSchema: z.ZodObject<{
48
+ version: z.ZodLiteral<1>;
49
+ text: z.ZodString;
50
+ metadata: z.ZodObject<{
51
+ version: z.ZodLiteral<1>;
52
+ repo: z.ZodString;
53
+ identity: z.ZodString;
54
+ baseRevision: z.ZodString;
55
+ knowledge: z.ZodObject<{
56
+ revision: z.ZodInt;
57
+ selectionSha256: z.ZodString;
58
+ references: z.ZodArray<z.ZodObject<{
59
+ id: z.ZodString;
60
+ title: z.ZodString;
61
+ path: z.ZodNullable<z.ZodString>;
62
+ sourceSha: z.ZodNullable<z.ZodString>;
63
+ sourceRevision: z.ZodNullable<z.ZodString>;
64
+ }, z.core.$strict>>;
65
+ omitted: z.ZodArray<z.ZodObject<{
66
+ title: z.ZodString;
67
+ reason: z.ZodString;
68
+ }, z.core.$strict>>;
69
+ }, z.core.$strict>;
70
+ skills: z.ZodObject<{
71
+ revision: z.ZodInt;
72
+ packages: z.ZodArray<z.ZodObject<{
73
+ name: z.ZodString;
74
+ sha256: z.ZodString;
75
+ skillFile: z.ZodString;
76
+ }, z.core.$strict>>;
77
+ }, z.core.$strict>;
78
+ directory: z.ZodNullable<z.ZodString>;
79
+ files: z.ZodArray<z.ZodObject<{
80
+ path: z.ZodString;
81
+ sha256: z.ZodString;
82
+ }, z.core.$strict>>;
83
+ }, z.core.$strict>;
84
+ sha256: z.ZodString;
85
+ }, z.core.$strict>;
86
+ export type CodingContextMetadata = z.infer<typeof codingContextMetadataSchema>;
87
+ export type CodingContext = z.infer<typeof codingContextSchema>;
88
+ /**
89
+ * A capture saved before the envelope carried `version` (every session through 0.9.36), as version 1. Readers then
90
+ * read only these three fields; the metadata is kept exactly as saved, since its bytes are what the digest covers.
91
+ */
92
+ export declare function upgradeUnversionedContext(body: Record<string, unknown>): Record<string, unknown>;
93
+ export declare const CODING_CONTEXT_UPGRADES: {
94
+ readonly 0: typeof upgradeUnversionedContext;
95
+ };
96
+ /** Read a saved capture: version 1 as itself, an unversioned one upgraded, a newer one refused plainly. */
97
+ export declare function readCodingContext(input: unknown): ContractResult<CodingContext>;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The managed context a desktop coding session captures once (`prepareCodingContext` in coding-context.ts): the
3
+ * frozen text the agent is given, what it was selected from, and the digest that ties the two together. It is saved
4
+ * inside the session's workspace record and checked again (`verifyCodingContext`) before every resume.
5
+ *
6
+ * `metadata` keeps its own `version: 1` because it is part of the digest; its keys are listed in the order the digest
7
+ * was taken in, so a parsed capture digests exactly as it was saved. Repository identity, digest and the materialized
8
+ * skill files are checked in plain code after parsing.
9
+ */
10
+ import { z } from "zod";
11
+ import { readVersioned, versioned } from "./contract.js";
12
+ export const codingContextMetadataSchema = z.strictObject({
13
+ version: z.literal(1),
14
+ repo: z.string(),
15
+ identity: z.string(),
16
+ baseRevision: z.string(),
17
+ knowledge: z.strictObject({
18
+ revision: z.int(),
19
+ selectionSha256: z.string(),
20
+ references: z.array(z.strictObject({
21
+ id: z.string(),
22
+ title: z.string(),
23
+ path: z.string().nullable(),
24
+ sourceSha: z.string().nullable(),
25
+ sourceRevision: z.string().nullable(),
26
+ })),
27
+ omitted: z.array(z.strictObject({ title: z.string(), reason: z.string() })),
28
+ }),
29
+ skills: z.strictObject({
30
+ revision: z.int(),
31
+ packages: z.array(z.strictObject({ name: z.string(), sha256: z.string(), skillFile: z.string() })),
32
+ }),
33
+ directory: z.string().nullable(),
34
+ files: z.array(z.strictObject({ path: z.string(), sha256: z.string() })),
35
+ });
36
+ export const CODING_CONTEXT_VERSION = 1;
37
+ export const codingContextSchema = versioned(CODING_CONTEXT_VERSION, {
38
+ text: z.string(),
39
+ metadata: codingContextMetadataSchema,
40
+ /** learningSha of `{ text, metadata }`. */
41
+ sha256: z.string(),
42
+ });
43
+ const CONTEXT_FIELDS = ["text", "metadata", "sha256"];
44
+ /**
45
+ * A capture saved before the envelope carried `version` (every session through 0.9.36), as version 1. Readers then
46
+ * read only these three fields; the metadata is kept exactly as saved, since its bytes are what the digest covers.
47
+ */
48
+ export function upgradeUnversionedContext(body) {
49
+ const out = { version: CODING_CONTEXT_VERSION };
50
+ for (const field of CONTEXT_FIELDS) {
51
+ if (Object.prototype.hasOwnProperty.call(body, field) && body[field] !== undefined)
52
+ out[field] = body[field];
53
+ }
54
+ return out;
55
+ }
56
+ export const CODING_CONTEXT_UPGRADES = { 0: upgradeUnversionedContext };
57
+ /** Read a saved capture: version 1 as itself, an unversioned one upgraded, a newer one refused plainly. */
58
+ export function readCodingContext(input) {
59
+ return readVersioned(codingContextSchema, input, CODING_CONTEXT_UPGRADES, { distinguishNull: true });
60
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The coding handoff receipt: the immutable terms under which a committed desktop coding session became an ordinary
3
+ * review task (`coding_handoff.payload`, coding-handoff.ts). Its bytes are hashed and sealed, and its id is the hash of
4
+ * its terms, so the keys are listed in the order the receipt has always been written in — `version` after the
5
+ * preview — and a parsed receipt serializes to exactly the bytes it was read from. The saved hash, derived identity,
6
+ * branch and scope seal are checked in plain code: the hash before parsing, the rest after.
7
+ */
8
+ import { z } from "zod";
9
+ import { type ContractResult } from "./contract.js";
10
+ export declare const CODING_HANDOFF_VERSION = 1;
11
+ export declare const codingHandoffReceiptSchema: z.ZodObject<{
12
+ sessionId: z.ZodString;
13
+ repo: z.ZodString;
14
+ base: z.ZodString;
15
+ candidate: z.ZodString;
16
+ title: z.ZodString;
17
+ originalPrompt: z.ZodString;
18
+ changedPaths: z.ZodArray<z.ZodString>;
19
+ version: z.ZodLiteral<1>;
20
+ identity: z.ZodString;
21
+ actor: z.ZodString;
22
+ generation: z.ZodInt;
23
+ goal: z.ZodString;
24
+ acceptance: z.ZodArray<z.ZodObject<{
25
+ id: z.ZodString;
26
+ statement: z.ZodString;
27
+ how: z.ZodOptional<z.ZodNullable<z.ZodString>>;
28
+ evidence: z.ZodArray<z.ZodEnum<{
29
+ "changed-path": "changed-path";
30
+ check: "check";
31
+ "manual-review": "manual-review";
32
+ screenshot: "screenshot";
33
+ }>>;
34
+ }, z.core.$strict>>;
35
+ outOfScope: z.ZodString;
36
+ id: z.ZodString;
37
+ taskId: z.ZodString;
38
+ branch: z.ZodString;
39
+ }, z.core.$strict>;
40
+ export type CodingHandoffReceipt = z.infer<typeof codingHandoffReceiptSchema>;
41
+ /** Read a receipt: version 1 as itself, a newer one refused plainly. Receipts have always carried `version`. */
42
+ export declare function readCodingHandoffReceipt(input: unknown): ContractResult<CodingHandoffReceipt>;
43
+ /** Read a receipt's saved bytes: JSON first, then the schema. */
44
+ export declare function parseCodingHandoffReceipt(raw: string): ContractResult<CodingHandoffReceipt>;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The coding handoff receipt: the immutable terms under which a committed desktop coding session became an ordinary
3
+ * review task (`coding_handoff.payload`, coding-handoff.ts). Its bytes are hashed and sealed, and its id is the hash of
4
+ * its terms, so the keys are listed in the order the receipt has always been written in — `version` after the
5
+ * preview — and a parsed receipt serializes to exactly the bytes it was read from. The saved hash, derived identity,
6
+ * branch and scope seal are checked in plain code: the hash before parsing, the rest after.
7
+ */
8
+ import { z } from "zod";
9
+ import { readVersioned } from "./contract.js";
10
+ import { acceptanceCriterionSchema } from "./plan.js";
11
+ export const CODING_HANDOFF_VERSION = 1;
12
+ export const codingHandoffReceiptSchema = z.strictObject({
13
+ sessionId: z.string(),
14
+ repo: z.string(),
15
+ base: z.string(),
16
+ candidate: z.string(),
17
+ title: z.string(),
18
+ /** The session's first message, as the person wrote it. */
19
+ originalPrompt: z.string(),
20
+ changedPaths: z.array(z.string()),
21
+ version: z.literal(CODING_HANDOFF_VERSION),
22
+ identity: z.string(),
23
+ actor: z.string(),
24
+ generation: z.int(),
25
+ goal: z.string(),
26
+ acceptance: z.array(acceptanceCriterionSchema),
27
+ outOfScope: z.string(),
28
+ /** The first 32 hex of learningSha over every field above, in this order. */
29
+ id: z.string(),
30
+ taskId: z.string(),
31
+ branch: z.string(),
32
+ });
33
+ /** Read a receipt: version 1 as itself, a newer one refused plainly. Receipts have always carried `version`. */
34
+ export function readCodingHandoffReceipt(input) {
35
+ return readVersioned(codingHandoffReceiptSchema, input, {}, { distinguishNull: true });
36
+ }
37
+ /** Read a receipt's saved bytes: JSON first, then the schema. */
38
+ export function parseCodingHandoffReceipt(raw) {
39
+ let body;
40
+ try {
41
+ body = JSON.parse(raw);
42
+ }
43
+ catch {
44
+ return { ok: false, issues: [{ path: "payload", kind: "invalid", line: "payload: not JSON" }] };
45
+ }
46
+ return readCodingHandoffReceipt(body);
47
+ }
@@ -0,0 +1,165 @@
1
+ /**
2
+ * The workspace record: one desktop coding session as the coding catalog saves it (`coding_session.document`). The
3
+ * session itself is what the console, chat and the coding handoff read; the record is that session with its
4
+ * `version`. Row ownership, generation and recovery state are checked in plain code by the workspace after parsing.
5
+ */
6
+ import { z } from "zod";
7
+ import { type ContractResult } from "./contract.js";
8
+ export declare const CODING_STATUSES: readonly ["starting", "ready", "working", "needs-input", "stopping", "interrupted", "failed", "uncertain", "closed"];
9
+ export declare const codingSessionSchema: z.ZodObject<{
10
+ id: z.ZodString;
11
+ owner: z.ZodString;
12
+ generation: z.ZodInt;
13
+ repo: z.ZodString;
14
+ title: z.ZodString;
15
+ provider: z.ZodLiteral<"codex">;
16
+ model: z.ZodNullable<z.ZodString>;
17
+ branch: z.ZodString;
18
+ base: z.ZodString;
19
+ worktree: z.ZodString;
20
+ nativeThreadId: z.ZodNullable<z.ZodString>;
21
+ turnId: z.ZodNullable<z.ZodString>;
22
+ status: z.ZodEnum<{
23
+ closed: "closed";
24
+ failed: "failed";
25
+ interrupted: "interrupted";
26
+ "needs-input": "needs-input";
27
+ ready: "ready";
28
+ starting: "starting";
29
+ stopping: "stopping";
30
+ uncertain: "uncertain";
31
+ working: "working";
32
+ }>;
33
+ error: z.ZodNullable<z.ZodString>;
34
+ createdAt: z.ZodString;
35
+ updatedAt: z.ZodString;
36
+ deliveryReviewRequired: z.ZodOptional<z.ZodBoolean>;
37
+ initialRequest: z.ZodOptional<z.ZodObject<{
38
+ requestId: z.ZodString;
39
+ prompt: z.ZodString;
40
+ }, z.core.$strict>>;
41
+ context: z.ZodOptional<z.ZodObject<{
42
+ version: z.ZodLiteral<1>;
43
+ text: z.ZodString;
44
+ metadata: z.ZodObject<{
45
+ version: z.ZodLiteral<1>;
46
+ repo: z.ZodString;
47
+ identity: z.ZodString;
48
+ baseRevision: z.ZodString;
49
+ knowledge: z.ZodObject<{
50
+ revision: z.ZodInt;
51
+ selectionSha256: z.ZodString;
52
+ references: z.ZodArray<z.ZodObject<{
53
+ id: z.ZodString;
54
+ title: z.ZodString;
55
+ path: z.ZodNullable<z.ZodString>;
56
+ sourceSha: z.ZodNullable<z.ZodString>;
57
+ sourceRevision: z.ZodNullable<z.ZodString>;
58
+ }, z.core.$strict>>;
59
+ omitted: z.ZodArray<z.ZodObject<{
60
+ title: z.ZodString;
61
+ reason: z.ZodString;
62
+ }, z.core.$strict>>;
63
+ }, z.core.$strict>;
64
+ skills: z.ZodObject<{
65
+ revision: z.ZodInt;
66
+ packages: z.ZodArray<z.ZodObject<{
67
+ name: z.ZodString;
68
+ sha256: z.ZodString;
69
+ skillFile: z.ZodString;
70
+ }, z.core.$strict>>;
71
+ }, z.core.$strict>;
72
+ directory: z.ZodNullable<z.ZodString>;
73
+ files: z.ZodArray<z.ZodObject<{
74
+ path: z.ZodString;
75
+ sha256: z.ZodString;
76
+ }, z.core.$strict>>;
77
+ }, z.core.$strict>;
78
+ sha256: z.ZodString;
79
+ }, z.core.$strict>>;
80
+ }, z.core.$strict>;
81
+ export declare const CODING_SESSION_VERSION = 1;
82
+ export declare const codingSessionRecordSchema: z.ZodObject<{
83
+ version: z.ZodLiteral<1>;
84
+ id: z.ZodString;
85
+ owner: z.ZodString;
86
+ generation: z.ZodInt;
87
+ repo: z.ZodString;
88
+ title: z.ZodString;
89
+ provider: z.ZodLiteral<"codex">;
90
+ model: z.ZodNullable<z.ZodString>;
91
+ branch: z.ZodString;
92
+ base: z.ZodString;
93
+ worktree: z.ZodString;
94
+ nativeThreadId: z.ZodNullable<z.ZodString>;
95
+ turnId: z.ZodNullable<z.ZodString>;
96
+ status: z.ZodEnum<{
97
+ closed: "closed";
98
+ failed: "failed";
99
+ interrupted: "interrupted";
100
+ "needs-input": "needs-input";
101
+ ready: "ready";
102
+ starting: "starting";
103
+ stopping: "stopping";
104
+ uncertain: "uncertain";
105
+ working: "working";
106
+ }>;
107
+ error: z.ZodNullable<z.ZodString>;
108
+ createdAt: z.ZodString;
109
+ updatedAt: z.ZodString;
110
+ deliveryReviewRequired: z.ZodOptional<z.ZodBoolean>;
111
+ initialRequest: z.ZodOptional<z.ZodObject<{
112
+ requestId: z.ZodString;
113
+ prompt: z.ZodString;
114
+ }, z.core.$strict>>;
115
+ context: z.ZodOptional<z.ZodObject<{
116
+ version: z.ZodLiteral<1>;
117
+ text: z.ZodString;
118
+ metadata: z.ZodObject<{
119
+ version: z.ZodLiteral<1>;
120
+ repo: z.ZodString;
121
+ identity: z.ZodString;
122
+ baseRevision: z.ZodString;
123
+ knowledge: z.ZodObject<{
124
+ revision: z.ZodInt;
125
+ selectionSha256: z.ZodString;
126
+ references: z.ZodArray<z.ZodObject<{
127
+ id: z.ZodString;
128
+ title: z.ZodString;
129
+ path: z.ZodNullable<z.ZodString>;
130
+ sourceSha: z.ZodNullable<z.ZodString>;
131
+ sourceRevision: z.ZodNullable<z.ZodString>;
132
+ }, z.core.$strict>>;
133
+ omitted: z.ZodArray<z.ZodObject<{
134
+ title: z.ZodString;
135
+ reason: z.ZodString;
136
+ }, z.core.$strict>>;
137
+ }, z.core.$strict>;
138
+ skills: z.ZodObject<{
139
+ revision: z.ZodInt;
140
+ packages: z.ZodArray<z.ZodObject<{
141
+ name: z.ZodString;
142
+ sha256: z.ZodString;
143
+ skillFile: z.ZodString;
144
+ }, z.core.$strict>>;
145
+ }, z.core.$strict>;
146
+ directory: z.ZodNullable<z.ZodString>;
147
+ files: z.ZodArray<z.ZodObject<{
148
+ path: z.ZodString;
149
+ sha256: z.ZodString;
150
+ }, z.core.$strict>>;
151
+ }, z.core.$strict>;
152
+ sha256: z.ZodString;
153
+ }, z.core.$strict>>;
154
+ }, z.core.$strict>;
155
+ export type CodingStatus = z.infer<typeof codingSessionSchema>["status"];
156
+ export type CodingSession = z.infer<typeof codingSessionSchema>;
157
+ /**
158
+ * Unversioned records were read as a cast, including partial fields, nulls and unknown nested keys. Preserve that
159
+ * boundary exactly; only records explicitly written as version 1 are subject to its strict schema.
160
+ */
161
+ export declare function readCodingSessionRecord(input: unknown): ContractResult<CodingSession>;
162
+ /** Read a saved `coding_session.document`, throwing a path-named refusal; the catalog was written by Toolroll alone. */
163
+ export declare function parseCodingSessionDocument(document: string): CodingSession;
164
+ /** Stamp the current version only if the entire record fits, without dropping any legacy fields. */
165
+ export declare function codingSessionDocument(session: CodingSession): string;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * The workspace record: one desktop coding session as the coding catalog saves it (`coding_session.document`). The
3
+ * session itself is what the console, chat and the coding handoff read; the record is that session with its
4
+ * `version`. Row ownership, generation and recovery state are checked in plain code by the workspace after parsing.
5
+ */
6
+ import { z } from "zod";
7
+ import { readVersioned, versioned } from "./contract.js";
8
+ import { CODING_CONTEXT_VERSION, codingContextSchema } from "./coding-context.js";
9
+ export const CODING_STATUSES = ["starting", "ready", "working", "needs-input", "stopping", "interrupted", "failed", "uncertain", "closed"];
10
+ export const codingSessionSchema = z.strictObject({
11
+ id: z.string(),
12
+ owner: z.string(),
13
+ generation: z.int(),
14
+ repo: z.string(),
15
+ title: z.string(),
16
+ provider: z.literal("codex"),
17
+ model: z.string().nullable(),
18
+ branch: z.string(),
19
+ base: z.string(),
20
+ worktree: z.string(),
21
+ nativeThreadId: z.string().nullable(),
22
+ turnId: z.string().nullable(),
23
+ status: z.enum(CODING_STATUSES),
24
+ error: z.string().nullable(),
25
+ createdAt: z.string(),
26
+ updatedAt: z.string(),
27
+ /** Set once startup or the last message could not be confirmed; absent on sessions that never needed it. */
28
+ deliveryReviewRequired: z.boolean().optional(),
29
+ /** The request the session started with, kept so it is never resent. */
30
+ initialRequest: z.strictObject({ requestId: z.string(), prompt: z.string() }).optional(),
31
+ /** Absent when no managed context was captured. */
32
+ context: codingContextSchema.optional(),
33
+ });
34
+ export const CODING_SESSION_VERSION = 1;
35
+ export const codingSessionRecordSchema = versioned(CODING_SESSION_VERSION, codingSessionSchema.shape);
36
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
37
+ /**
38
+ * Unversioned records were read as a cast, including partial fields, nulls and unknown nested keys. Preserve that
39
+ * boundary exactly; only records explicitly written as version 1 are subject to its strict schema.
40
+ */
41
+ export function readCodingSessionRecord(input) {
42
+ if (isRecord(input) && !Object.prototype.hasOwnProperty.call(input, "version"))
43
+ return { ok: true, value: input };
44
+ const read = readVersioned(codingSessionRecordSchema, input, {}, { distinguishNull: true });
45
+ if (!read.ok)
46
+ return read;
47
+ const { version: _version, ...session } = read.value;
48
+ return { ok: true, value: session };
49
+ }
50
+ /** Read a saved `coding_session.document`, throwing a path-named refusal; the catalog was written by Toolroll alone. */
51
+ export function parseCodingSessionDocument(document) {
52
+ let body;
53
+ try {
54
+ body = JSON.parse(document);
55
+ }
56
+ catch {
57
+ throw Error("A saved coding session could not be read: payload: not JSON");
58
+ }
59
+ const read = readCodingSessionRecord(body);
60
+ if (!read.ok)
61
+ throw Error(`A saved coding session could not be read: ${read.issues.map(issue => issue.line).join("; ")}`);
62
+ return read.value;
63
+ }
64
+ /** Stamp the current version only if the entire record fits, without dropping any legacy fields. */
65
+ export function codingSessionDocument(session) {
66
+ const record = { version: CODING_SESSION_VERSION, ...session };
67
+ if (isRecord(record.context) && !Object.prototype.hasOwnProperty.call(record.context, "version")) {
68
+ record.context = { ...record.context, version: CODING_CONTEXT_VERSION };
69
+ }
70
+ return JSON.stringify(codingSessionRecordSchema.safeParse(record).success ? record : session);
71
+ }
@@ -12,9 +12,12 @@ import { type TextLimitKey } from "../text-limits.js";
12
12
  * A string bounded by its TEXT_LIMITS entry, never a literal. The limit rule rides along as the field's description,
13
13
  * so a model reads the bound before it writes. Zod counts UTF-16 code units; for a limit named in bytes this is the
14
14
  * schema's necessary bound (a string within N bytes is within N code units), and the byte count itself is checked in
15
- * plain code after parsing.
15
+ * plain code after parsing. `shorten: false` is for a contract with no shorten turn (a parked decision): its
16
+ * description states the bound without promising one.
16
17
  */
17
- export declare function limited(field: string, limitKey: TextLimitKey): z.ZodString;
18
+ export declare function limited(field: string, limitKey: TextLimitKey, options?: {
19
+ shorten?: boolean;
20
+ }): z.ZodString;
18
21
  /** A versioned payload envelope: a strict object whose `version` is exactly `version`. */
19
22
  export declare function versioned<V extends number, S extends z.ZodRawShape>(version: V, shape: S): z.ZodObject<{
20
23
  version: z.ZodLiteral<V>;
@@ -36,6 +39,8 @@ export declare function pathOf(path: readonly PropertyKey[]): string;
36
39
  */
37
40
  export type ContractOptions = {
38
41
  aliases?: Readonly<Record<string, readonly string[]>>;
42
+ /** Report explicit null as a wrong value; omitted keeps the historical wording for existing contracts. */
43
+ distinguishNull?: boolean;
39
44
  };
40
45
  type Where = {
41
46
  schema?: z.ZodType;