@shipwithai/loopnote 0.0.0-stage → 2.0.0-bootstrap.0

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 (151) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +202 -2
  3. package/THIRD_PARTY_NOTICES.md +580 -0
  4. package/dist/bin/loopnote.mjs +5 -0
  5. package/dist/cli/confirm.js +38 -0
  6. package/dist/cli/picker.js +342 -0
  7. package/dist/cli/request.js +120 -0
  8. package/dist/cli/run.js +970 -0
  9. package/dist/contracts/api.js +640 -0
  10. package/dist/contracts/comparison.js +178 -0
  11. package/dist/contracts/draft.js +606 -0
  12. package/dist/contracts/request.js +124 -0
  13. package/dist/domain/anchor.js +122 -0
  14. package/dist/domain/comparison-key.js +51 -0
  15. package/dist/domain/errors.js +43 -0
  16. package/dist/domain/path-bytes.js +82 -0
  17. package/dist/domain/product-identity.js +66 -0
  18. package/dist/domain/source.js +4 -0
  19. package/dist/draft/mutate-draft.js +87 -0
  20. package/dist/export/render-review-markdown.js +163 -0
  21. package/dist/export/review-export.js +273 -0
  22. package/dist/git/availability.js +92 -0
  23. package/dist/git/candidates.js +413 -0
  24. package/dist/git/comparison.js +263 -0
  25. package/dist/git/exact-patch.js +702 -0
  26. package/dist/git/ignore-status.js +23 -0
  27. package/dist/git/inventory.js +160 -0
  28. package/dist/git/numstat.js +111 -0
  29. package/dist/git/objects.js +95 -0
  30. package/dist/git/raw-diff.js +84 -0
  31. package/dist/git/repository.js +144 -0
  32. package/dist/git/runner.js +145 -0
  33. package/dist/git/selector-drift.js +169 -0
  34. package/dist/git/standalone-resolution.js +174 -0
  35. package/dist/git/worktree-content.js +97 -0
  36. package/dist/git/worktree-inventory.js +391 -0
  37. package/dist/native/directory_exchange.node +0 -0
  38. package/dist/server/app.js +171 -0
  39. package/dist/server/attached-completion.js +85 -0
  40. package/dist/server/capabilities.js +1572 -0
  41. package/dist/server/draft-loader.js +134 -0
  42. package/dist/server/draft-recovery.js +5 -0
  43. package/dist/server/draft-store.js +380 -0
  44. package/dist/server/export-store.js +291 -0
  45. package/dist/server/gitignore-capability.js +197 -0
  46. package/dist/server/image-content.js +101 -0
  47. package/dist/server/lifecycle.js +72 -0
  48. package/dist/server/native-exchange-capability.js +45 -0
  49. package/dist/server/patch-snapshot.js +500 -0
  50. package/dist/server/routes.js +418 -0
  51. package/dist/server/security.js +123 -0
  52. package/dist/server/snapshot-fs.js +58 -0
  53. package/dist/server/support-client.js +76 -0
  54. package/dist/server/support-store.js +194 -0
  55. package/dist/server/worktree-snapshot.js +553 -0
  56. package/dist/web/assets/abap-08VXUWAP.js +1 -0
  57. package/dist/web/assets/apex-BWPQTe0t.js +1 -0
  58. package/dist/web/assets/azcli-Bc_sGQ0U.js +1 -0
  59. package/dist/web/assets/bat-i0X4ZdIN.js +1 -0
  60. package/dist/web/assets/bicep-B5-_aFwp.js +2 -0
  61. package/dist/web/assets/cameligo-DMUM7wLl.js +1 -0
  62. package/dist/web/assets/clojure-Cm7r79vr.js +1 -0
  63. package/dist/web/assets/codicon-ngg6Pgfi.ttf +0 -0
  64. package/dist/web/assets/coffee-Ba7i2nA0.js +1 -0
  65. package/dist/web/assets/cpp-C7h46wYY.js +1 -0
  66. package/dist/web/assets/csharp-BKxtCVv1.js +1 -0
  67. package/dist/web/assets/csp-bTuwJoIa.js +1 -0
  68. package/dist/web/assets/css-DIMkf-bt.js +3 -0
  69. package/dist/web/assets/css.worker-yKcq13f2.js +89 -0
  70. package/dist/web/assets/cssMode-CpBTfhMg.js +1 -0
  71. package/dist/web/assets/cypher-CVaqCwHa.js +1 -0
  72. package/dist/web/assets/dart-onAF5SnQ.js +1 -0
  73. package/dist/web/assets/dockerfile-DZFCIeNp.js +1 -0
  74. package/dist/web/assets/ecl-D05T4iGw.js +1 -0
  75. package/dist/web/assets/editor.worker-tytyn86m.js +26 -0
  76. package/dist/web/assets/elixir-6RTg0lbw.js +1 -0
  77. package/dist/web/assets/flow9-C5_-GSwl.js +1 -0
  78. package/dist/web/assets/freemarker2-skQ63wb7.js +3 -0
  79. package/dist/web/assets/fsharp-C8Ef5oNN.js +1 -0
  80. package/dist/web/assets/go-C-y9NEjX.js +1 -0
  81. package/dist/web/assets/graphql-fmXr3nnJ.js +1 -0
  82. package/dist/web/assets/handlebars-CqvPAZY8.js +1 -0
  83. package/dist/web/assets/hcl-CpzslTdj.js +1 -0
  84. package/dist/web/assets/html-BDAqstpo.js +1 -0
  85. package/dist/web/assets/html.worker-DSJ9zmd7.js +502 -0
  86. package/dist/web/assets/htmlMode-BRYA3pnT.js +1 -0
  87. package/dist/web/assets/index-CCeYBqPo.js +1111 -0
  88. package/dist/web/assets/index-CRj9ojWJ.css +1 -0
  89. package/dist/web/assets/ini-sBoK_t0W.js +1 -0
  90. package/dist/web/assets/java-BEtHBSE6.js +1 -0
  91. package/dist/web/assets/javascript-1Ywytw67.js +1 -0
  92. package/dist/web/assets/json.worker-Oa9UrkKE.js +58 -0
  93. package/dist/web/assets/jsonMode-AO1C_0kX.js +7 -0
  94. package/dist/web/assets/julia-Bri6UV-V.js +1 -0
  95. package/dist/web/assets/kotlin-BOotOW0E.js +1 -0
  96. package/dist/web/assets/less-B9JPFI3C.js +2 -0
  97. package/dist/web/assets/lexon-CfSJPG6W.js +1 -0
  98. package/dist/web/assets/liquid-CkkqyyGc.js +1 -0
  99. package/dist/web/assets/lspLanguageFeatures-CYOFEMZp.js +4 -0
  100. package/dist/web/assets/lua-CsQS60Ue.js +1 -0
  101. package/dist/web/assets/m3-D-oSqn_W.js +1 -0
  102. package/dist/web/assets/markdown-Cimd5fb3.js +1 -0
  103. package/dist/web/assets/mdx-Bd3Wlfqw.js +1 -0
  104. package/dist/web/assets/mips-CIPQ_RoX.js +1 -0
  105. package/dist/web/assets/msdax-DauUninz.js +1 -0
  106. package/dist/web/assets/mysql-SOo6toE5.js +1 -0
  107. package/dist/web/assets/objective-c-FvmIjYaQ.js +1 -0
  108. package/dist/web/assets/pascal-DrH0SRf2.js +1 -0
  109. package/dist/web/assets/pascaligo-D-ptJ9y-.js +1 -0
  110. package/dist/web/assets/perl-oz_6vUea.js +1 -0
  111. package/dist/web/assets/pgsql-DTj74zXo.js +1 -0
  112. package/dist/web/assets/php-nr791fC2.js +1 -0
  113. package/dist/web/assets/pla-CopQ2nXW.js +1 -0
  114. package/dist/web/assets/postiats-43DmfD33.js +1 -0
  115. package/dist/web/assets/powerquery-D3hlyOfw.js +1 -0
  116. package/dist/web/assets/powershell-DmHpPYUd.js +1 -0
  117. package/dist/web/assets/protobuf-C531GsRP.js +2 -0
  118. package/dist/web/assets/pug-Z5eAx3Zn.js +1 -0
  119. package/dist/web/assets/python-CET4IumD.js +1 -0
  120. package/dist/web/assets/qsharp-DkqhCAOL.js +1 -0
  121. package/dist/web/assets/r-BwWrilGY.js +1 -0
  122. package/dist/web/assets/razor-d-reaHeT.js +1 -0
  123. package/dist/web/assets/redis-ClamHrr6.js +1 -0
  124. package/dist/web/assets/redshift-DT7zqm-g.js +1 -0
  125. package/dist/web/assets/restructuredtext-BYgofb2h.js +1 -0
  126. package/dist/web/assets/ruby-DezsRK8O.js +1 -0
  127. package/dist/web/assets/rust-DdL9SqIa.js +1 -0
  128. package/dist/web/assets/sb-CcwsVR0C.js +1 -0
  129. package/dist/web/assets/scala-DHpiXF5c.js +1 -0
  130. package/dist/web/assets/scheme-BeGwcela.js +1 -0
  131. package/dist/web/assets/scss-gp-XZpBa.js +3 -0
  132. package/dist/web/assets/shell-CC2rA5mh.js +1 -0
  133. package/dist/web/assets/solidity-BEEn4gHE.js +1 -0
  134. package/dist/web/assets/sophia-CRfGWb83.js +1 -0
  135. package/dist/web/assets/sparql-D_Lu-MrJ.js +1 -0
  136. package/dist/web/assets/sql-NEE52Syq.js +1 -0
  137. package/dist/web/assets/st-DbInun42.js +1 -0
  138. package/dist/web/assets/swift-Bxkupp3x.js +1 -0
  139. package/dist/web/assets/systemverilog-Bz4Y3fRF.js +1 -0
  140. package/dist/web/assets/tcl-DISqw1ZD.js +1 -0
  141. package/dist/web/assets/ts.worker-BtjBFNHX.js +67734 -0
  142. package/dist/web/assets/tsMode-DLxSAtkD.js +11 -0
  143. package/dist/web/assets/twig-De2hgUGE.js +1 -0
  144. package/dist/web/assets/typescript-DprEVA-S.js +1 -0
  145. package/dist/web/assets/typespec-B8J7ngcE.js +1 -0
  146. package/dist/web/assets/vb-DV3o63ZY.js +1 -0
  147. package/dist/web/assets/wgsl-DpFanUEy.js +298 -0
  148. package/dist/web/assets/xml-B7ZwKOhJ.js +1 -0
  149. package/dist/web/assets/yaml-OQ9Asg7B.js +1 -0
  150. package/dist/web/index.html +14 -0
  151. package/package.json +87 -4
@@ -0,0 +1,640 @@
1
+ import { z } from 'zod';
2
+ import { RENAME_MAP } from '../domain/product-identity.js';
3
+ import { AvailabilitySchema, ChangedFileStatusKindSchema, ExactPatchValidationTargetSchema, ExactPathSchema, GitModeSchema, GitObjectIdSchema, RangeReviewScopeSchema, } from './comparison.js';
4
+ import { AnchorVerificationSchema, CommentBodySchema, CommentIdSchema, DraftComparisonSchema, DurableAnchorV1Schema, RevisionSchema, ReviewDraftV1Schema, SummaryMarkdownSchema, } from './draft.js';
5
+ export const OpaqueFileIdSchema = z.string().regex(/^file_[A-Za-z0-9_-]{43}$/);
6
+ const AnchorSideSchema = z.enum(['base', 'head']);
7
+ export const AddCommentRequestSchema = z
8
+ .strictObject({
9
+ type: z.literal('addComment'),
10
+ expectedRevision: RevisionSchema,
11
+ fileId: OpaqueFileIdSchema,
12
+ side: AnchorSideSchema,
13
+ line: z.number().int().positive(),
14
+ endLine: z.number().int().positive().optional(),
15
+ body: CommentBodySchema,
16
+ })
17
+ .superRefine((value, context) => {
18
+ if (value.endLine !== undefined && value.endLine < value.line) {
19
+ context.addIssue({ code: 'custom', message: 'endLine must be >= line.', path: ['endLine'] });
20
+ }
21
+ })
22
+ .readonly();
23
+ export const DraftMutationRequestSchema = z
24
+ .discriminatedUnion('type', [
25
+ AddCommentRequestSchema,
26
+ z.strictObject({
27
+ type: z.literal('editComment'),
28
+ expectedRevision: RevisionSchema,
29
+ commentId: CommentIdSchema,
30
+ body: CommentBodySchema,
31
+ }),
32
+ z.strictObject({
33
+ type: z.literal('deleteComment'),
34
+ expectedRevision: RevisionSchema,
35
+ commentId: CommentIdSchema,
36
+ }),
37
+ z.strictObject({
38
+ type: z.literal('resolveComment'),
39
+ expectedRevision: RevisionSchema,
40
+ commentId: CommentIdSchema,
41
+ }),
42
+ z.strictObject({
43
+ type: z.literal('reopenComment'),
44
+ expectedRevision: RevisionSchema,
45
+ commentId: CommentIdSchema,
46
+ }),
47
+ z.strictObject({
48
+ type: z.literal('setSummary'),
49
+ expectedRevision: RevisionSchema,
50
+ markdown: SummaryMarkdownSchema,
51
+ }),
52
+ ])
53
+ .readonly();
54
+ const DraftCommentViewSchema = z
55
+ .discriminatedUnion('state', [
56
+ z.strictObject({
57
+ id: CommentIdSchema,
58
+ state: z.literal('open'),
59
+ body: CommentBodySchema,
60
+ anchor: DurableAnchorV1Schema,
61
+ createdAt: z.string().datetime(),
62
+ updatedAt: z.string().datetime(),
63
+ verification: AnchorVerificationSchema,
64
+ }),
65
+ z.strictObject({
66
+ id: CommentIdSchema,
67
+ state: z.literal('resolved'),
68
+ body: CommentBodySchema,
69
+ anchor: DurableAnchorV1Schema,
70
+ createdAt: z.string().datetime(),
71
+ updatedAt: z.string().datetime(),
72
+ resolvedAt: z.string().datetime(),
73
+ verification: AnchorVerificationSchema,
74
+ }),
75
+ ])
76
+ .readonly();
77
+ export const DraftViewSchema = z
78
+ .strictObject({
79
+ schemaVersion: z.literal(1),
80
+ comparison: DraftComparisonSchema,
81
+ revision: RevisionSchema,
82
+ summary: SummaryMarkdownSchema,
83
+ comments: z.array(DraftCommentViewSchema).max(10_000).readonly(),
84
+ })
85
+ .readonly();
86
+ const SAFE_DRAFT_PATH_PATTERN = new RegExp(`^\\${RENAME_MAP.repoStateDir}drafts/[A-Za-z0-9._-]+$`, 'u');
87
+ export const SafeDraftPathSchema = z.string().regex(SAFE_DRAFT_PATH_PATTERN);
88
+ const DraftMalformedLoadSchema = z
89
+ .strictObject({
90
+ kind: z.literal('malformed'),
91
+ path: SafeDraftPathSchema,
92
+ fingerprint: z.string().regex(/^[0-9a-f]{64}$/u),
93
+ detail: z.strictObject({ message: z.string().min(1).max(160) }).readonly(),
94
+ })
95
+ .readonly();
96
+ const DraftSchemaInvalidLoadSchema = z
97
+ .strictObject({
98
+ kind: z.literal('schemaInvalid'),
99
+ path: SafeDraftPathSchema,
100
+ fingerprint: z.string().regex(/^[0-9a-f]{64}$/u),
101
+ details: z
102
+ .array(z.strictObject({ path: z.string().max(160), message: z.string().min(1).max(160) }).readonly())
103
+ .min(1)
104
+ .max(8)
105
+ .readonly(),
106
+ })
107
+ .readonly();
108
+ const DraftNewerUnsupportedLoadSchema = z
109
+ .strictObject({
110
+ kind: z.literal('newerUnsupported'),
111
+ path: SafeDraftPathSchema,
112
+ foundVersion: z.number().int().positive(),
113
+ supportedVersion: z.literal(1),
114
+ })
115
+ .readonly();
116
+ export const DraftLoadResponseSchema = z
117
+ .discriminatedUnion('kind', [
118
+ z.strictObject({ kind: z.literal('missing'), path: SafeDraftPathSchema }).readonly(),
119
+ z.strictObject({ kind: z.literal('current'), path: SafeDraftPathSchema, draft: DraftViewSchema }).readonly(),
120
+ DraftMalformedLoadSchema,
121
+ DraftSchemaInvalidLoadSchema,
122
+ DraftNewerUnsupportedLoadSchema,
123
+ ])
124
+ .readonly();
125
+ const DraftReadOnlyLoadSchema = z.union([
126
+ DraftMalformedLoadSchema,
127
+ DraftSchemaInvalidLoadSchema,
128
+ DraftNewerUnsupportedLoadSchema,
129
+ ]);
130
+ export const DraftMutationAcceptedSchema = z
131
+ .strictObject({
132
+ kind: z.literal('accepted'),
133
+ draft: ReviewDraftV1Schema,
134
+ })
135
+ .readonly();
136
+ export const DraftMutationConflictSchema = z
137
+ .strictObject({
138
+ kind: z.literal('revisionConflict'),
139
+ expectedRevision: RevisionSchema,
140
+ actualRevision: RevisionSchema,
141
+ latest: ReviewDraftV1Schema,
142
+ })
143
+ .readonly();
144
+ export const DraftMutationFailureSchema = z
145
+ .discriminatedUnion('kind', [
146
+ z.strictObject({ kind: z.literal('invalidTarget') }),
147
+ z.strictObject({ kind: z.literal('illegalTransition') }),
148
+ z.strictObject({ kind: z.literal('persistenceFailure') }),
149
+ z.strictObject({ kind: z.literal('readOnly'), load: DraftReadOnlyLoadSchema }),
150
+ ])
151
+ .readonly();
152
+ export const DraftMutationResultSchema = z
153
+ .union([DraftMutationAcceptedSchema, DraftMutationConflictSchema, DraftMutationFailureSchema])
154
+ .readonly();
155
+ export const DraftRecoveryRequestSchema = z
156
+ .strictObject({ expectedFingerprint: z.string().regex(/^[0-9a-f]{64}$/u) })
157
+ .readonly();
158
+ export const DraftRecoveryResultSchema = z
159
+ .discriminatedUnion('kind', [
160
+ z.strictObject({
161
+ kind: z.literal('recovered'),
162
+ backupPath: SafeDraftPathSchema,
163
+ draft: ReviewDraftV1Schema,
164
+ }).readonly(),
165
+ z.strictObject({ kind: z.literal('fingerprintChanged') }).readonly(),
166
+ z.strictObject({ kind: z.literal('recoveryUnavailable'), load: DraftLoadResponseSchema }).readonly(),
167
+ z.strictObject({ kind: z.literal('persistenceFailure') }).readonly(),
168
+ ])
169
+ .readonly();
170
+ export const DraftRevealResultSchema = z
171
+ .union([
172
+ z.strictObject({ kind: z.literal('revealed') }).readonly(),
173
+ z.strictObject({ kind: z.literal('revealFailed') }).readonly(),
174
+ ])
175
+ .readonly();
176
+ export const UncommittedDraftRecoveryResultSchema = z
177
+ .discriminatedUnion('kind', [
178
+ z.strictObject({ kind: z.literal('recovered') }).readonly(),
179
+ z.strictObject({ kind: z.literal('unavailable') }).readonly(),
180
+ ])
181
+ .readonly();
182
+ export const ExportReviewRequestSchema = z
183
+ .strictObject({
184
+ expectedRevision: RevisionSchema,
185
+ driftAcknowledgementToken: z.string().regex(/^[A-Za-z0-9_-]{43,128}$/u).optional(),
186
+ })
187
+ .readonly();
188
+ export const ExportDirectoryRevealResultSchema = z
189
+ .union([
190
+ z.strictObject({ kind: z.literal('revealed') }).readonly(),
191
+ z.strictObject({ kind: z.literal('revealFailed') }).readonly(),
192
+ ])
193
+ .readonly();
194
+ export const LoopnoteIgnoreStatusSchema = z
195
+ .discriminatedUnion('kind', [
196
+ z.strictObject({ kind: z.literal('ignored') }).readonly(),
197
+ z.strictObject({ kind: z.literal('notIgnored') }).readonly(),
198
+ z.strictObject({ kind: z.literal('unavailable') }).readonly(),
199
+ ])
200
+ .readonly();
201
+ export const AppendLoopnoteIgnoreResultSchema = z
202
+ .discriminatedUnion('kind', [
203
+ z.strictObject({ kind: z.literal('appended') }).readonly(),
204
+ z.strictObject({ kind: z.literal('alreadyIgnored') }).readonly(),
205
+ z.strictObject({ kind: z.literal('unconfirmed') }).readonly(),
206
+ z.strictObject({ kind: z.literal('unchanged') }).readonly(),
207
+ z.strictObject({ kind: z.literal('appendUnconfirmed') }).readonly(),
208
+ z.strictObject({ kind: z.literal('ambiguous') }).readonly(),
209
+ ])
210
+ .readonly();
211
+ const ExistingFileContentSideSchema = z
212
+ .strictObject({
213
+ exists: z.literal(true),
214
+ path: ExactPathSchema,
215
+ language: z.string().min(1),
216
+ blobOid: GitObjectIdSchema,
217
+ text: z.string(),
218
+ })
219
+ .readonly();
220
+ const MissingFileContentSideSchema = z
221
+ .strictObject({
222
+ exists: z.literal(false),
223
+ })
224
+ .readonly();
225
+ export const FileContentResponseSchema = z
226
+ .strictObject({
227
+ fileId: OpaqueFileIdSchema,
228
+ base: z.union([ExistingFileContentSideSchema, MissingFileContentSideSchema]),
229
+ head: z.union([ExistingFileContentSideSchema, MissingFileContentSideSchema]),
230
+ })
231
+ .readonly();
232
+ const ApiWorktreeIdentitySchema = z
233
+ .strictObject({
234
+ path: z.string().min(1),
235
+ dirty: z.boolean(),
236
+ })
237
+ .readonly();
238
+ const ApiPinnedEndpointSchema = z
239
+ .strictObject({
240
+ label: z.string().min(1),
241
+ oid: GitObjectIdSchema,
242
+ worktree: ApiWorktreeIdentitySchema.optional(),
243
+ })
244
+ .readonly();
245
+ const SessionRangeSchema = z
246
+ .strictObject({
247
+ kind: z.literal('revisions'),
248
+ baseOid: GitObjectIdSchema,
249
+ headOid: GitObjectIdSchema,
250
+ pathspecs: z.array(z.string()).readonly(),
251
+ })
252
+ .readonly();
253
+ const ApiFileStatusSchema = z
254
+ .strictObject({
255
+ kind: ChangedFileStatusKindSchema,
256
+ similarity: z.number().int().min(0).max(100).optional(),
257
+ })
258
+ .readonly();
259
+ export const SessionFileSchema = z
260
+ .strictObject({
261
+ fileId: OpaqueFileIdSchema,
262
+ status: ApiFileStatusSchema,
263
+ oldMode: GitModeSchema,
264
+ oldPath: ExactPathSchema.optional(),
265
+ newPath: ExactPathSchema.optional(),
266
+ additions: z.number().int().nonnegative().nullable(),
267
+ deletions: z.number().int().nonnegative().nullable(),
268
+ availability: AvailabilitySchema,
269
+ standaloneGitState: z
270
+ .enum(['modified-uncommitted', 'untracked', 'unchanged'])
271
+ .optional(),
272
+ })
273
+ .readonly();
274
+ export const AttachedCompletionStatusSchema = z
275
+ .discriminatedUnion('kind', [
276
+ z.strictObject({ kind: z.literal('waiting') }).readonly(),
277
+ z.strictObject({ kind: z.literal('finishing'), expectedRevision: RevisionSchema }).readonly(),
278
+ z.strictObject({ kind: z.literal('completed'), revision: RevisionSchema }).readonly(),
279
+ ])
280
+ .readonly();
281
+ export const FinishReviewRequestSchema = z
282
+ .strictObject({ expectedRevision: RevisionSchema })
283
+ .readonly();
284
+ export const FinishReviewResultSchema = z
285
+ .discriminatedUnion('kind', [
286
+ z.strictObject({ kind: z.literal('completed'), revision: RevisionSchema }).readonly(),
287
+ z.strictObject({ kind: z.literal('alreadyCompleted'), revision: RevisionSchema }).readonly(),
288
+ z.strictObject({
289
+ kind: z.literal('revisionConflict'),
290
+ expectedRevision: RevisionSchema,
291
+ actualRevision: RevisionSchema,
292
+ }).readonly(),
293
+ z.strictObject({
294
+ kind: z.literal('staleAnchors'),
295
+ affectedCommentIds: z.array(CommentIdSchema).readonly(),
296
+ affectedCount: z.number().int().nonnegative(),
297
+ }).readonly(),
298
+ z.strictObject({ kind: z.literal('scopeInvalid') }).readonly(),
299
+ z.strictObject({ kind: z.literal('draftReadOnly') }).readonly(),
300
+ z.strictObject({ kind: z.literal('persistenceFailure') }).readonly(),
301
+ z.strictObject({ kind: z.literal('canonicalizationFailure') }).readonly(),
302
+ z.strictObject({ kind: z.literal('deliveryFailed') }).readonly(),
303
+ ])
304
+ .readonly();
305
+ const AttachedSessionMarkerSchema = z
306
+ .strictObject({ kind: z.literal('agent-review') })
307
+ .readonly();
308
+ export const SessionSupportCapabilitySchema = z
309
+ .strictObject({ enabled: z.literal(true) })
310
+ .readonly();
311
+ const PinnedSessionResponseSchema = z
312
+ .strictObject({
313
+ base: ApiPinnedEndpointSchema,
314
+ head: ApiPinnedEndpointSchema,
315
+ mergeBaseOid: GitObjectIdSchema,
316
+ range: SessionRangeSchema.optional(),
317
+ files: z.array(SessionFileSchema).readonly(),
318
+ attached: AttachedSessionMarkerSchema.optional(),
319
+ support: SessionSupportCapabilitySchema.optional(),
320
+ repoName: z.string().optional(),
321
+ })
322
+ .superRefine((session, context) => {
323
+ if (session.range !== undefined &&
324
+ (session.range.baseOid !== session.base.oid ||
325
+ session.range.headOid !== session.head.oid)) {
326
+ context.addIssue({
327
+ code: z.ZodIssueCode.custom,
328
+ message: 'Range scope must match pinned session endpoints.',
329
+ });
330
+ }
331
+ })
332
+ .readonly();
333
+ const ExactPatchSessionResponseSchema = z
334
+ .strictObject({
335
+ patch: z
336
+ .strictObject({
337
+ kind: z.literal('exact-patch'),
338
+ digest: z.string().regex(/^[0-9a-f]{64}$/u),
339
+ reviewKey: z.string().regex(/^[0-9a-f]{64}$/u),
340
+ validationTarget: ExactPatchValidationTargetSchema,
341
+ changedFileCount: z.number().int().nonnegative(),
342
+ })
343
+ .readonly(),
344
+ files: z.array(SessionFileSchema).readonly(),
345
+ attached: AttachedSessionMarkerSchema.optional(),
346
+ support: SessionSupportCapabilitySchema.optional(),
347
+ repoName: z.string().optional(),
348
+ })
349
+ .readonly();
350
+ const UncommittedSessionResponseSchema = z
351
+ .strictObject({
352
+ uncommitted: z
353
+ .strictObject({
354
+ kind: z.literal('uncommitted'),
355
+ sourceKind: z.enum(['staged', 'unstaged', 'worktree', 'standalone']),
356
+ baseCommitOid: GitObjectIdSchema.optional(),
357
+ reviewKey: z.string().regex(/^[0-9a-f]{64}$/u),
358
+ changedFileCount: z.number().int().nonnegative(),
359
+ recoveryAvailable: z.literal(true).optional(),
360
+ })
361
+ .readonly(),
362
+ files: z.array(SessionFileSchema).readonly(),
363
+ attached: AttachedSessionMarkerSchema.optional(),
364
+ support: SessionSupportCapabilitySchema.optional(),
365
+ repoName: z.string().optional(),
366
+ })
367
+ .readonly();
368
+ export const SessionResponseSchema = z
369
+ .union([PinnedSessionResponseSchema, ExactPatchSessionResponseSchema, UncommittedSessionResponseSchema])
370
+ .readonly();
371
+ export const SelectorDriftRoleSchema = z.enum(['base', 'head']);
372
+ export const SelectorTypeSchema = z.enum(['branch', 'worktree']);
373
+ export const SelectorUnavailableReasonSchema = z.literal('source-unavailable');
374
+ export const SelectorDriftStatusSchema = z
375
+ .discriminatedUnion('kind', [
376
+ z
377
+ .strictObject({
378
+ kind: z.literal('unchanged'),
379
+ role: SelectorDriftRoleSchema,
380
+ })
381
+ .readonly(),
382
+ z
383
+ .strictObject({
384
+ kind: z.literal('moved'),
385
+ role: SelectorDriftRoleSchema,
386
+ label: z.string().min(1),
387
+ selectorType: SelectorTypeSchema,
388
+ oldOid: GitObjectIdSchema,
389
+ newOid: GitObjectIdSchema,
390
+ })
391
+ .readonly(),
392
+ z
393
+ .strictObject({
394
+ kind: z.literal('unavailable'),
395
+ role: SelectorDriftRoleSchema,
396
+ label: z.string().min(1),
397
+ selectorType: SelectorTypeSchema,
398
+ oldOid: GitObjectIdSchema,
399
+ reason: SelectorUnavailableReasonSchema,
400
+ })
401
+ .readonly(),
402
+ ])
403
+ .readonly();
404
+ export const SelectorDriftResponseSchema = z
405
+ .strictObject({
406
+ base: SelectorDriftStatusSchema,
407
+ head: SelectorDriftStatusSchema,
408
+ })
409
+ .readonly();
410
+ export const PatchStatusResponseSchema = z
411
+ .discriminatedUnion('kind', [
412
+ z
413
+ .strictObject({
414
+ kind: z.literal('unchanged'),
415
+ validationTargetLabel: z.string().min(1),
416
+ })
417
+ .readonly(),
418
+ z
419
+ .strictObject({
420
+ kind: z.literal('drifted'),
421
+ validationTargetLabel: z.string().min(1),
422
+ })
423
+ .readonly(),
424
+ z.strictObject({ kind: z.literal('snapshotUnavailable') }).readonly(),
425
+ ])
426
+ .readonly();
427
+ const ExportReceiptDirectoryPattern = new RegExp(`^\\${RENAME_MAP.repoStateDir}exports/((?:(?:[0-9a-f]{40}|[0-9a-f]{64})\\.\\.(?:[0-9a-f]{40}|[0-9a-f]{64}))|(?:[0-9a-f]{64}|agent-[0-9a-f]{32}))/review\\.(?:json|md)$`, 'u');
428
+ const ExportReceiptJsonFileSchema = z
429
+ .strictObject({
430
+ path: z
431
+ .string()
432
+ .regex(new RegExp(`^\\${RENAME_MAP.repoStateDir}exports/(?:(?:[0-9a-f]{40}|[0-9a-f]{64})\\.\\.(?:[0-9a-f]{40}|[0-9a-f]{64})|(?:[0-9a-f]{64}|agent-[0-9a-f]{32}))/review\\.json$`, 'u')),
433
+ algorithm: z.literal('sha256'),
434
+ sha256: z.string().regex(/^[0-9a-f]{64}$/u),
435
+ bytes: z.number().int().nonnegative(),
436
+ })
437
+ .readonly();
438
+ const ExportReceiptMarkdownFileSchema = z
439
+ .strictObject({
440
+ path: z
441
+ .string()
442
+ .regex(new RegExp(`^\\${RENAME_MAP.repoStateDir}exports/(?:(?:[0-9a-f]{40}|[0-9a-f]{64})\\.\\.(?:[0-9a-f]{40}|[0-9a-f]{64})|(?:[0-9a-f]{64}|agent-[0-9a-f]{32}))/review\\.md$`, 'u')),
443
+ algorithm: z.literal('sha256'),
444
+ sha256: z.string().regex(/^[0-9a-f]{64}$/u),
445
+ bytes: z.number().int().nonnegative(),
446
+ })
447
+ .readonly();
448
+ const ExportReceiptFilesSchema = z
449
+ .tuple([ExportReceiptJsonFileSchema, ExportReceiptMarkdownFileSchema])
450
+ .superRefine(([json, markdown], context) => {
451
+ const jsonDirectory = ExportReceiptDirectoryPattern.exec(json.path)?.[1];
452
+ const markdownDirectory = ExportReceiptDirectoryPattern.exec(markdown.path)?.[1];
453
+ if (jsonDirectory !== markdownDirectory) {
454
+ context.addIssue({
455
+ code: 'custom',
456
+ message: 'Export receipt files must share one comparison directory.',
457
+ path: [1, 'path'],
458
+ });
459
+ }
460
+ })
461
+ .readonly();
462
+ const ExportReceiptPinnedIdentitySchema = z
463
+ .strictObject({
464
+ label: z.string().min(1),
465
+ selectorType: SelectorTypeSchema,
466
+ oid: GitObjectIdSchema,
467
+ })
468
+ .readonly();
469
+ const ExportReceiptCurrentIdentitySchema = z
470
+ .discriminatedUnion('kind', [
471
+ z.strictObject({
472
+ kind: z.literal('available'),
473
+ label: z.string().min(1),
474
+ selectorType: SelectorTypeSchema,
475
+ oid: GitObjectIdSchema,
476
+ }).readonly(),
477
+ z.strictObject({
478
+ kind: z.literal('unavailable'),
479
+ label: z.string().min(1),
480
+ selectorType: SelectorTypeSchema,
481
+ reason: SelectorUnavailableReasonSchema,
482
+ }).readonly(),
483
+ ])
484
+ .readonly();
485
+ const ExportReceiptDriftIdentitySchema = z
486
+ .strictObject({
487
+ role: SelectorDriftRoleSchema,
488
+ pinned: ExportReceiptPinnedIdentitySchema,
489
+ current: ExportReceiptCurrentIdentitySchema,
490
+ })
491
+ .readonly();
492
+ const ExportReceiptDriftSchema = z
493
+ .discriminatedUnion('kind', [
494
+ z.strictObject({ kind: z.literal('noneObserved') }).readonly(),
495
+ z.strictObject({
496
+ kind: z.literal('acknowledged'),
497
+ identities: z.tuple([ExportReceiptDriftIdentitySchema, ExportReceiptDriftIdentitySchema]).readonly(),
498
+ }).readonly(),
499
+ ])
500
+ .readonly();
501
+ const ExportReceiptComparisonEndpointSchema = z
502
+ .strictObject({
503
+ label: z.string().min(1),
504
+ selectorType: SelectorTypeSchema.optional(),
505
+ oid: GitObjectIdSchema,
506
+ })
507
+ .readonly();
508
+ const ExportReceiptComparisonSchema = z
509
+ .strictObject({
510
+ base: ExportReceiptComparisonEndpointSchema,
511
+ head: ExportReceiptComparisonEndpointSchema,
512
+ })
513
+ .readonly();
514
+ const ComparisonExportReviewResultSchema = z.strictObject({
515
+ kind: z.literal('exported'),
516
+ draftRevision: RevisionSchema,
517
+ exportedAt: z.string().datetime(),
518
+ drift: ExportReceiptDriftSchema,
519
+ comparison: ExportReceiptComparisonSchema,
520
+ files: ExportReceiptFilesSchema,
521
+ }).superRefine((result, context) => {
522
+ if (result.drift.kind !== 'acknowledged')
523
+ return;
524
+ for (const role of ['base', 'head']) {
525
+ const identities = result.drift.identities.filter((identity) => identity.role === role);
526
+ if (identities.length !== 1) {
527
+ context.addIssue({
528
+ code: 'custom',
529
+ message: `Acknowledged drift requires exactly one ${role} identity.`,
530
+ path: ['drift', 'identities'],
531
+ });
532
+ continue;
533
+ }
534
+ const [identity] = identities;
535
+ const endpoint = result.comparison[role];
536
+ if (identity.pinned.label !== endpoint.label
537
+ || identity.pinned.selectorType !== endpoint.selectorType
538
+ || identity.pinned.oid !== endpoint.oid) {
539
+ context.addIssue({
540
+ code: 'custom',
541
+ message: `Acknowledged ${role} drift must match the pinned comparison endpoint.`,
542
+ path: ['drift', 'identities', result.drift.identities.indexOf(identity), 'pinned'],
543
+ });
544
+ }
545
+ }
546
+ if (!result.drift.identities.some((identity) => identity.current.kind === 'unavailable' || identity.current.oid !== identity.pinned.oid)) {
547
+ context.addIssue({
548
+ code: 'custom',
549
+ message: 'Acknowledged drift requires an unavailable or changed current endpoint.',
550
+ path: ['drift', 'identities'],
551
+ });
552
+ }
553
+ }).readonly();
554
+ const ExactPatchExportReviewResultSchema = z.strictObject({
555
+ kind: z.literal('exported'),
556
+ draftRevision: RevisionSchema,
557
+ exportedAt: z.string().datetime(),
558
+ patch: z.strictObject({
559
+ digest: z.string().regex(/^[0-9a-f]{64}$/u),
560
+ validationTarget: ExactPatchValidationTargetSchema,
561
+ reviewKey: z.string().regex(/^[0-9a-f]{64}$/u),
562
+ snapshot: z.strictObject({ status: z.enum(['unchanged', 'drifted']) }).readonly(),
563
+ }).readonly(),
564
+ files: ExportReceiptFilesSchema,
565
+ }).readonly();
566
+ const StandaloneExportReviewResultSchema = z.strictObject({
567
+ kind: z.literal('exported'),
568
+ draftRevision: RevisionSchema,
569
+ exportedAt: z.string().datetime(),
570
+ standalone: z.strictObject({
571
+ reviewKey: z.string().regex(/^[0-9a-f]{64}$/u),
572
+ }).readonly(),
573
+ files: ExportReceiptFilesSchema,
574
+ }).readonly();
575
+ export const ExportReviewResultSchema = z.union([
576
+ ComparisonExportReviewResultSchema,
577
+ ExactPatchExportReviewResultSchema,
578
+ StandaloneExportReviewResultSchema,
579
+ z.discriminatedUnion('kind', [
580
+ z.strictObject({
581
+ kind: z.literal('revisionConflict'),
582
+ expectedRevision: RevisionSchema,
583
+ actualRevision: RevisionSchema,
584
+ }).readonly(),
585
+ z.strictObject({
586
+ kind: z.literal('driftAcknowledgementRequired'),
587
+ acknowledgementToken: z.string().regex(/^[A-Za-z0-9_-]{43,128}$/u),
588
+ observation: SelectorDriftResponseSchema,
589
+ }).readonly(),
590
+ z.strictObject({
591
+ kind: z.literal('driftAcknowledgementStale'),
592
+ acknowledgementToken: z.string().regex(/^[A-Za-z0-9_-]{43,128}$/u),
593
+ observation: SelectorDriftResponseSchema,
594
+ }).readonly(),
595
+ z.strictObject({ kind: z.literal('draftReadOnly') }).readonly(),
596
+ z.strictObject({ kind: z.literal('reExportUnsupported') }).readonly(),
597
+ z.strictObject({ kind: z.literal('publicationFailed') }).readonly(),
598
+ z.strictObject({ kind: z.literal('recoveryRequired') }).readonly(),
599
+ ]),
600
+ ]).readonly();
601
+ export const FileMetadataResponseSchema = z
602
+ .strictObject({
603
+ fileId: OpaqueFileIdSchema,
604
+ status: ApiFileStatusSchema,
605
+ oldPath: ExactPathSchema.optional(),
606
+ newPath: ExactPathSchema.optional(),
607
+ additions: z.number().int().nonnegative().nullable(),
608
+ deletions: z.number().int().nonnegative().nullable(),
609
+ availability: AvailabilitySchema,
610
+ oldMode: GitModeSchema,
611
+ newMode: GitModeSchema,
612
+ })
613
+ .readonly();
614
+ export const SupportActionSchema = z.enum(['support', 'restore']);
615
+ export const SupportStartRequestSchema = z
616
+ .strictObject({ action: SupportActionSchema })
617
+ .readonly();
618
+ export const SupportStartResultSchema = z
619
+ .discriminatedUnion('kind', [
620
+ z
621
+ .strictObject({
622
+ kind: z.literal('ready'),
623
+ flowUrl: z
624
+ .string()
625
+ .url()
626
+ .refine((value) => new URL(value).protocol === 'https:'),
627
+ })
628
+ .readonly(),
629
+ z.strictObject({ kind: z.literal('unavailable') }).readonly(),
630
+ ])
631
+ .readonly();
632
+ export const SupportStatusSchema = z
633
+ .strictObject({ status: z.enum(['unverified', 'verified']) })
634
+ .readonly();
635
+ export const ApiErrorSchema = z
636
+ .strictObject({
637
+ code: z.enum(['request-unavailable', 'session-unavailable']),
638
+ message: z.string().min(1),
639
+ })
640
+ .readonly();