@peasant-labs/schema 0.1.0-rc6 → 0.1.0-rc9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -3
- package/dist/fixtures/timeline.d.ts +33 -2
- package/dist/internal/generated/contract/zod.gen.d.ts +803 -116
- package/dist/internal/generated/contract/zod.gen.js +470 -44
- package/dist/internal/generated/enums.gen.d.ts +91 -4
- package/dist/internal/generated/enums.gen.js +97 -1
- package/dist/internal/generated/local-api.d.ts +100 -1
- package/dist/internal/generated/public-contract.gen.d.ts +2 -2
- package/dist/internal/generated/public-contract.gen.js +1 -1
- package/dist/internal/generated/timeline-fixtures.gen.js +1014 -2
- package/dist/internal/generated/versions.gen.d.ts +3 -3
- package/dist/internal/generated/versions.gen.js +3 -3
- package/dist/internal/generated/village-api.d.ts +66 -1
- package/package.json +5 -1
|
@@ -29,7 +29,12 @@ export const zAnnotationDatatype = z.enum([
|
|
|
29
29
|
export const zAnnotationEntryTarget = z.object({
|
|
30
30
|
endIndex: z.int(),
|
|
31
31
|
entryIndex: z.int(),
|
|
32
|
-
sessionId: z.string()
|
|
32
|
+
sessionId: z.string().min(1)
|
|
33
|
+
}).superRefine((value, context) => {
|
|
34
|
+
if (value.sessionId === "")
|
|
35
|
+
context.addIssue({ code: "custom", message: "entry targets require a non-empty sessionId" });
|
|
36
|
+
if (value.endIndex <= value.entryIndex)
|
|
37
|
+
context.addIssue({ code: "custom", message: "entry targets require endIndex greater than entryIndex" });
|
|
33
38
|
});
|
|
34
39
|
export const zAnnotationManifestResponse = z.object({
|
|
35
40
|
digest: z.string(),
|
|
@@ -89,6 +94,98 @@ export const zAnnotatorSummary = z.object({
|
|
|
89
94
|
providerKey: z.string().nullish(),
|
|
90
95
|
status: z.string()
|
|
91
96
|
});
|
|
97
|
+
/**
|
|
98
|
+
* Association Conclusion
|
|
99
|
+
*
|
|
100
|
+
* Producer-supplied conclusion for a session-to-commit association: confirmed or candidate
|
|
101
|
+
*/
|
|
102
|
+
export const zAssociationConclusion = z.enum(['confirmed', 'candidate']);
|
|
103
|
+
/**
|
|
104
|
+
* Association Evidence Kind
|
|
105
|
+
*
|
|
106
|
+
* Atomic observation supporting a session-to-commit association
|
|
107
|
+
*/
|
|
108
|
+
export const zAssociationEvidenceKind = z.enum([
|
|
109
|
+
'recorded_commit',
|
|
110
|
+
'touched_file',
|
|
111
|
+
'branch_membership',
|
|
112
|
+
'time_window'
|
|
113
|
+
]);
|
|
114
|
+
export const zAssociationEvidenceObservation = z.object({
|
|
115
|
+
branchName: z.string().nullish(),
|
|
116
|
+
kind: zAssociationEvidenceKind,
|
|
117
|
+
recordedCommitHash: z.string().nullish(),
|
|
118
|
+
touchedFilePath: z.string().nullish(),
|
|
119
|
+
windowEndMs: z.int().nullish(),
|
|
120
|
+
windowStartMs: z.int().nullish()
|
|
121
|
+
}).superRefine((value, context) => {
|
|
122
|
+
const isAbsent = (detail) => detail === undefined || detail === null;
|
|
123
|
+
const isGoWhitespace = (codePoint) => (codePoint >= 0x0009 && codePoint <= 0x000D)
|
|
124
|
+
|| codePoint === 0x0020
|
|
125
|
+
|| codePoint === 0x0085
|
|
126
|
+
|| codePoint === 0x00A0
|
|
127
|
+
|| codePoint === 0x1680
|
|
128
|
+
|| (codePoint >= 0x2000 && codePoint <= 0x200A)
|
|
129
|
+
|| codePoint === 0x2028
|
|
130
|
+
|| codePoint === 0x2029
|
|
131
|
+
|| codePoint === 0x202F
|
|
132
|
+
|| codePoint === 0x205F
|
|
133
|
+
|| codePoint === 0x3000;
|
|
134
|
+
const hasNonEmptyString = (detail) => {
|
|
135
|
+
if (typeof detail !== "string")
|
|
136
|
+
return false;
|
|
137
|
+
for (const character of detail) {
|
|
138
|
+
const codePoint = character.codePointAt(0);
|
|
139
|
+
if (codePoint !== undefined && !isGoWhitespace(codePoint))
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
};
|
|
144
|
+
const hasValidTouchedFilePath = (detail) => typeof detail === "string"
|
|
145
|
+
&& detail !== ""
|
|
146
|
+
&& !detail.startsWith("/")
|
|
147
|
+
&& !detail.startsWith("\\")
|
|
148
|
+
&& !detail.includes("\\")
|
|
149
|
+
&& !/^[A-Za-z]:\//.test(detail)
|
|
150
|
+
&& !detail.split("/").some((segment) => segment === "" || segment === "." || segment === "..");
|
|
151
|
+
const addIssue = (message) => context.addIssue({ code: "custom", message });
|
|
152
|
+
switch (value.kind) {
|
|
153
|
+
case "recorded_commit":
|
|
154
|
+
if (!hasNonEmptyString(value.recordedCommitHash))
|
|
155
|
+
addIssue("recorded_commit evidence requires a non-empty recordedCommitHash");
|
|
156
|
+
if (!isAbsent(value.touchedFilePath) || !isAbsent(value.branchName) || !isAbsent(value.windowStartMs) || !isAbsent(value.windowEndMs))
|
|
157
|
+
addIssue("recorded_commit evidence must not populate another detail arm");
|
|
158
|
+
return;
|
|
159
|
+
case "touched_file":
|
|
160
|
+
if (!hasValidTouchedFilePath(value.touchedFilePath))
|
|
161
|
+
addIssue("touched_file evidence requires a non-empty repository-relative touchedFilePath");
|
|
162
|
+
if (!isAbsent(value.recordedCommitHash) || !isAbsent(value.branchName) || !isAbsent(value.windowStartMs) || !isAbsent(value.windowEndMs))
|
|
163
|
+
addIssue("touched_file evidence must not populate another detail arm");
|
|
164
|
+
return;
|
|
165
|
+
case "branch_membership":
|
|
166
|
+
if (!hasNonEmptyString(value.branchName))
|
|
167
|
+
addIssue("branch_membership evidence requires a non-empty branchName");
|
|
168
|
+
if (!isAbsent(value.recordedCommitHash) || !isAbsent(value.touchedFilePath) || !isAbsent(value.windowStartMs) || !isAbsent(value.windowEndMs))
|
|
169
|
+
addIssue("branch_membership evidence must not populate another detail arm");
|
|
170
|
+
return;
|
|
171
|
+
case "time_window":
|
|
172
|
+
if (value.windowStartMs === undefined || value.windowStartMs === null || value.windowEndMs === undefined || value.windowEndMs === null) {
|
|
173
|
+
addIssue("time_window evidence requires windowStartMs and windowEndMs");
|
|
174
|
+
}
|
|
175
|
+
else if (value.windowStartMs > value.windowEndMs) {
|
|
176
|
+
addIssue("time_window evidence requires windowStartMs less than or equal to windowEndMs");
|
|
177
|
+
}
|
|
178
|
+
if (!isAbsent(value.recordedCommitHash) || !isAbsent(value.touchedFilePath) || !isAbsent(value.branchName))
|
|
179
|
+
addIssue("time_window evidence must not populate another detail arm");
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
/**
|
|
184
|
+
* Association ID
|
|
185
|
+
*
|
|
186
|
+
* Opaque durable Peasant identifier for one session-to-commit association
|
|
187
|
+
*/
|
|
188
|
+
export const zAssociationID = z.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/);
|
|
92
189
|
export const zBatchCreateAnnotationsErrorResponse = z.object({
|
|
93
190
|
error: z.string(),
|
|
94
191
|
failingIndex: z.int()
|
|
@@ -189,6 +286,16 @@ export const zCommitInfo = z.object({
|
|
|
189
286
|
hash: z.string(),
|
|
190
287
|
message: z.string()
|
|
191
288
|
});
|
|
289
|
+
/**
|
|
290
|
+
* Confidence
|
|
291
|
+
*
|
|
292
|
+
* Strength of evidence behind a derived relationship: high, medium, or low
|
|
293
|
+
*/
|
|
294
|
+
export const zConfidence = z.enum([
|
|
295
|
+
'high',
|
|
296
|
+
'medium',
|
|
297
|
+
'low'
|
|
298
|
+
]);
|
|
192
299
|
/**
|
|
193
300
|
* Content Kind
|
|
194
301
|
*
|
|
@@ -352,13 +459,6 @@ export const zFrictionCluster = z.object({
|
|
|
352
459
|
label: z.string(),
|
|
353
460
|
sessions: z.int()
|
|
354
461
|
});
|
|
355
|
-
export const zGitContext = z.object({
|
|
356
|
-
branch: z.string().nullish(),
|
|
357
|
-
commits: z.array(zCommitInfo).optional(),
|
|
358
|
-
remote: z.string().nullish(),
|
|
359
|
-
tracking: z.string().nullish(),
|
|
360
|
-
worktree: z.string().nullish()
|
|
361
|
-
});
|
|
362
462
|
/**
|
|
363
463
|
* Harness
|
|
364
464
|
*
|
|
@@ -381,6 +481,30 @@ export const zHealthResponse = z.object({
|
|
|
381
481
|
* Sanitized, filesystem-safe identifier derived from git remote; contains only [a-zA-Z0-9._<>-]
|
|
382
482
|
*/
|
|
383
483
|
export const zHostSlug = z.string().regex(/^[a-zA-Z0-9._<>-]+$/);
|
|
484
|
+
export const zInsightClassification = z.object({
|
|
485
|
+
category: z.string(),
|
|
486
|
+
cause: z.string(),
|
|
487
|
+
resolution: z.string(),
|
|
488
|
+
severityLocus: z.string(),
|
|
489
|
+
severityScope: z.string()
|
|
490
|
+
});
|
|
491
|
+
/**
|
|
492
|
+
* Insight Kind
|
|
493
|
+
*
|
|
494
|
+
* What a SessionInsight observed: a decision, friction, an unusual rate elevation, or a retry loop
|
|
495
|
+
*/
|
|
496
|
+
export const zInsightKind = z.enum([
|
|
497
|
+
'decision',
|
|
498
|
+
'friction',
|
|
499
|
+
'unusual',
|
|
500
|
+
'retry_loop'
|
|
501
|
+
]);
|
|
502
|
+
/**
|
|
503
|
+
* Insight Provenance
|
|
504
|
+
*
|
|
505
|
+
* How a SessionInsight was produced: mechanical (rule-derived) or mined
|
|
506
|
+
*/
|
|
507
|
+
export const zInsightProvenance = z.enum(['mechanical', 'mined']);
|
|
384
508
|
/**
|
|
385
509
|
* Interaction Type
|
|
386
510
|
*
|
|
@@ -417,26 +541,6 @@ export const zMapNodeKind = z.enum([
|
|
|
417
541
|
'package',
|
|
418
542
|
'file'
|
|
419
543
|
]);
|
|
420
|
-
export const zMapNode = z.object({
|
|
421
|
-
effortDensity: z.number(),
|
|
422
|
-
fileCount: z.int(),
|
|
423
|
-
id: z.string(),
|
|
424
|
-
kind: zMapNodeKind,
|
|
425
|
-
language: z.string().optional(),
|
|
426
|
-
layer: z.int(),
|
|
427
|
-
loc: z.int(),
|
|
428
|
-
name: z.string(),
|
|
429
|
-
order: z.int(),
|
|
430
|
-
parent: z.string().optional(),
|
|
431
|
-
recordedFiles: z.int(),
|
|
432
|
-
totalFiles: z.int(),
|
|
433
|
-
touchCount: z.int()
|
|
434
|
-
});
|
|
435
|
-
export const zMapSlice = z.object({
|
|
436
|
-
activityEdges: z.array(zActivityEdge).nullable(),
|
|
437
|
-
nodes: z.array(zMapNode).nullable(),
|
|
438
|
-
structureEdges: z.array(zMapEdge).nullable()
|
|
439
|
-
});
|
|
440
544
|
/**
|
|
441
545
|
* Message Type
|
|
442
546
|
*
|
|
@@ -483,18 +587,6 @@ export const zModelInfo = z.object({
|
|
|
483
587
|
* SHA-256 hex digest of the project's origin URL or local path
|
|
484
588
|
*/
|
|
485
589
|
export const zProjectHash = z.string().regex(/^[0-9a-f]{64}$/).brand();
|
|
486
|
-
export const zMapGraphPayload = z.object({
|
|
487
|
-
activityEdges: z.array(zActivityEdge).nullable(),
|
|
488
|
-
atCommit: z.string().optional(),
|
|
489
|
-
generatedAtMs: z.coerce.bigint().min(BigInt('-9223372036854775808'), { error: 'Invalid value: Expected int64 to be >= -9223372036854775808' }).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }),
|
|
490
|
-
nodes: z.array(zMapNode).nullable(),
|
|
491
|
-
parsedLanguages: z.array(z.string()).nullable(),
|
|
492
|
-
projectHash: zProjectHash,
|
|
493
|
-
repoFound: z.boolean(),
|
|
494
|
-
repoPath: z.string().optional(),
|
|
495
|
-
structureEdges: z.array(zMapEdge).nullable(),
|
|
496
|
-
violations: z.array(zEdgeViolation).nullable()
|
|
497
|
-
});
|
|
498
590
|
export const zProjectContext = z.object({
|
|
499
591
|
filePath: z.string().optional(),
|
|
500
592
|
hash: zProjectHash,
|
|
@@ -530,6 +622,102 @@ export const zPublishResponse = z.object({
|
|
|
530
622
|
transcriptId: z.string(),
|
|
531
623
|
updatedAt: z.coerce.bigint().min(BigInt('-9223372036854775808'), { error: 'Invalid value: Expected int64 to be >= -9223372036854775808' }).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' })
|
|
532
624
|
});
|
|
625
|
+
export const zPublishedAssociation = z.object({
|
|
626
|
+
id: zAssociationID,
|
|
627
|
+
observedCommitHash: z.string()
|
|
628
|
+
});
|
|
629
|
+
export const zGitContext = z.object({
|
|
630
|
+
associations: z.array(zPublishedAssociation).optional(),
|
|
631
|
+
branch: z.string().nullish(),
|
|
632
|
+
commits: z.array(zCommitInfo).optional(),
|
|
633
|
+
remote: z.string().nullish(),
|
|
634
|
+
tracking: z.string().nullish(),
|
|
635
|
+
worktree: z.string().nullish()
|
|
636
|
+
}).superRefine((value, context) => {
|
|
637
|
+
if (value.associations === undefined || value.associations === null)
|
|
638
|
+
return;
|
|
639
|
+
const seenIDs = new Map();
|
|
640
|
+
const seenObservedHashes = new Map();
|
|
641
|
+
for (const [index, association] of value.associations.entries()) {
|
|
642
|
+
if (association.observedCommitHash.trim() === "") {
|
|
643
|
+
context.addIssue({ code: "custom", path: ["associations", index, "observedCommitHash"], message: "published associations require a non-empty observedCommitHash" });
|
|
644
|
+
}
|
|
645
|
+
const priorID = seenIDs.get(association.id);
|
|
646
|
+
if (priorID !== undefined) {
|
|
647
|
+
context.addIssue({ code: "custom", path: ["associations", index, "id"], message: "published association IDs must be unique within one request" });
|
|
648
|
+
}
|
|
649
|
+
else {
|
|
650
|
+
seenIDs.set(association.id, index);
|
|
651
|
+
}
|
|
652
|
+
const priorObservedHash = seenObservedHashes.get(association.observedCommitHash);
|
|
653
|
+
if (priorObservedHash !== undefined) {
|
|
654
|
+
context.addIssue({ code: "custom", path: ["associations", index, "observedCommitHash"], message: "published association observedCommitHash values must be unique within one request" });
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
seenObservedHashes.set(association.observedCommitHash, index);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
/**
|
|
662
|
+
* Read Attribution State
|
|
663
|
+
*
|
|
664
|
+
* Whether per-file read attribution is recoverable for a node's editing sessions: complete, partial, or unavailable
|
|
665
|
+
*/
|
|
666
|
+
export const zReadAttributionState = z.enum([
|
|
667
|
+
'complete',
|
|
668
|
+
'partial',
|
|
669
|
+
'unavailable'
|
|
670
|
+
]);
|
|
671
|
+
/**
|
|
672
|
+
* Read State Grade
|
|
673
|
+
*
|
|
674
|
+
* Ordinal explicit read-state act: none, viewed, reviewed, or reviewed_in_detail
|
|
675
|
+
*/
|
|
676
|
+
export const zReadStateGrade = z.enum([
|
|
677
|
+
'none',
|
|
678
|
+
'viewed',
|
|
679
|
+
'reviewed',
|
|
680
|
+
'reviewed_in_detail'
|
|
681
|
+
]);
|
|
682
|
+
export const zMapNode = z.object({
|
|
683
|
+
agentEditedCount: z.int(),
|
|
684
|
+
attributedRegionCount: z.int(),
|
|
685
|
+
changedRegionCount: z.int(),
|
|
686
|
+
effortDensity: z.number(),
|
|
687
|
+
fileCount: z.int(),
|
|
688
|
+
id: z.string(),
|
|
689
|
+
kind: zMapNodeKind,
|
|
690
|
+
language: z.string().optional(),
|
|
691
|
+
layer: z.int(),
|
|
692
|
+
loc: z.int(),
|
|
693
|
+
name: z.string(),
|
|
694
|
+
order: z.int(),
|
|
695
|
+
parent: z.string().optional(),
|
|
696
|
+
readAttribution: zReadAttributionState,
|
|
697
|
+
readCount: z.int(),
|
|
698
|
+
readState: zReadStateGrade,
|
|
699
|
+
recordedFiles: z.int(),
|
|
700
|
+
reviewedRegionCount: z.int(),
|
|
701
|
+
totalFiles: z.int(),
|
|
702
|
+
touchCount: z.int()
|
|
703
|
+
});
|
|
704
|
+
export const zMapGraphPayload = z.object({
|
|
705
|
+
activityEdges: z.array(zActivityEdge).nullable(),
|
|
706
|
+
atCommit: z.string().optional(),
|
|
707
|
+
generatedAtMs: z.coerce.bigint().min(BigInt('-9223372036854775808'), { error: 'Invalid value: Expected int64 to be >= -9223372036854775808' }).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }),
|
|
708
|
+
nodes: z.array(zMapNode).nullable(),
|
|
709
|
+
parsedLanguages: z.array(z.string()).nullable(),
|
|
710
|
+
projectHash: zProjectHash,
|
|
711
|
+
repoFound: z.boolean(),
|
|
712
|
+
repoPath: z.string().optional(),
|
|
713
|
+
structureEdges: z.array(zMapEdge).nullable(),
|
|
714
|
+
violations: z.array(zEdgeViolation).nullable()
|
|
715
|
+
});
|
|
716
|
+
export const zMapSlice = z.object({
|
|
717
|
+
activityEdges: z.array(zActivityEdge).nullable(),
|
|
718
|
+
nodes: z.array(zMapNode).nullable(),
|
|
719
|
+
structureEdges: z.array(zMapEdge).nullable()
|
|
720
|
+
});
|
|
533
721
|
export const zRedactionInfo = z.object({
|
|
534
722
|
applied: z.boolean(),
|
|
535
723
|
content_hash_at_redact: z.string().optional(),
|
|
@@ -543,6 +731,29 @@ export const zReviewSuggestion = z.object({
|
|
|
543
731
|
path: z.string(),
|
|
544
732
|
suggestedPrompt: z.string()
|
|
545
733
|
});
|
|
734
|
+
/**
|
|
735
|
+
* Rewrite Method
|
|
736
|
+
*
|
|
737
|
+
* Mechanism the resolver used to map a ghost commit to its successor
|
|
738
|
+
*/
|
|
739
|
+
export const zRewriteMethod = z.enum([
|
|
740
|
+
'hash',
|
|
741
|
+
'patch_id',
|
|
742
|
+
'author_identity',
|
|
743
|
+
'message_embedded',
|
|
744
|
+
'temporal',
|
|
745
|
+
'none'
|
|
746
|
+
]);
|
|
747
|
+
/**
|
|
748
|
+
* Rewrite Resolution
|
|
749
|
+
*
|
|
750
|
+
* Whether a ledger-observed commit hash is live, was rewritten, or could not be resolved
|
|
751
|
+
*/
|
|
752
|
+
export const zRewriteResolution = z.enum([
|
|
753
|
+
'live',
|
|
754
|
+
'rewritten',
|
|
755
|
+
'unresolved'
|
|
756
|
+
]);
|
|
546
757
|
/**
|
|
547
758
|
* Role
|
|
548
759
|
*
|
|
@@ -601,18 +812,95 @@ export const zServerMessage = z.object({
|
|
|
601
812
|
* Unique session identifier (UUID, agent-{hex}, ses_{id}, sess_{id} (ACP), or msg_{id})
|
|
602
813
|
*/
|
|
603
814
|
export const zSessionID = z.string().regex(/^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|agent-[a-f0-9]+|sess?_[a-zA-Z0-9]+|msg_[a-zA-Z0-9]+)$/);
|
|
815
|
+
export const zInsightEvidence = z.object({
|
|
816
|
+
commitHash: z.string().optional(),
|
|
817
|
+
entryIndex: z.int().nullish(),
|
|
818
|
+
file: z.string().optional(),
|
|
819
|
+
sessionId: zSessionID
|
|
820
|
+
});
|
|
821
|
+
export const zSessionAssociation = z.object({
|
|
822
|
+
conclusion: zAssociationConclusion,
|
|
823
|
+
confidence: zConfidence,
|
|
824
|
+
evidence: z.array(zAssociationEvidenceObservation).min(1),
|
|
825
|
+
id: zAssociationID,
|
|
826
|
+
sessionId: z.string().min(1)
|
|
827
|
+
}).superRefine((value, context) => {
|
|
828
|
+
const kindOrder = ["recorded_commit", "touched_file", "branch_membership", "time_window"];
|
|
829
|
+
const compareStrings = (left, right) => {
|
|
830
|
+
const leftBytes = new TextEncoder().encode(left);
|
|
831
|
+
const rightBytes = new TextEncoder().encode(right);
|
|
832
|
+
const sharedLength = Math.min(leftBytes.length, rightBytes.length);
|
|
833
|
+
for (let index = 0; index < sharedLength; index += 1) {
|
|
834
|
+
const leftByte = leftBytes[index];
|
|
835
|
+
const rightByte = rightBytes[index];
|
|
836
|
+
if (leftByte < rightByte)
|
|
837
|
+
return -1;
|
|
838
|
+
if (leftByte > rightByte)
|
|
839
|
+
return 1;
|
|
840
|
+
}
|
|
841
|
+
return compareNumbers(leftBytes.length, rightBytes.length);
|
|
842
|
+
};
|
|
843
|
+
const compareNumbers = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
844
|
+
const compareObservations = (left, right) => {
|
|
845
|
+
const leftKindOrder = kindOrder.indexOf(left.kind);
|
|
846
|
+
const rightKindOrder = kindOrder.indexOf(right.kind);
|
|
847
|
+
if (leftKindOrder !== rightKindOrder)
|
|
848
|
+
return compareNumbers(leftKindOrder, rightKindOrder);
|
|
849
|
+
switch (left.kind) {
|
|
850
|
+
case "recorded_commit": return compareStrings(typeof left.recordedCommitHash === "string" ? left.recordedCommitHash : "", typeof right.recordedCommitHash === "string" ? right.recordedCommitHash : "");
|
|
851
|
+
case "touched_file": return compareStrings(typeof left.touchedFilePath === "string" ? left.touchedFilePath : "", typeof right.touchedFilePath === "string" ? right.touchedFilePath : "");
|
|
852
|
+
case "branch_membership": return compareStrings(typeof left.branchName === "string" ? left.branchName : "", typeof right.branchName === "string" ? right.branchName : "");
|
|
853
|
+
case "time_window": {
|
|
854
|
+
const startOrder = compareNumbers(left.windowStartMs ?? 0, right.windowStartMs ?? 0);
|
|
855
|
+
return startOrder === 0 ? compareNumbers(left.windowEndMs ?? 0, right.windowEndMs ?? 0) : startOrder;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
return 0;
|
|
859
|
+
};
|
|
860
|
+
for (let index = 1; index < value.evidence.length; index += 1) {
|
|
861
|
+
const previous = value.evidence[index - 1];
|
|
862
|
+
const current = value.evidence[index];
|
|
863
|
+
if (previous === undefined || current === undefined)
|
|
864
|
+
continue;
|
|
865
|
+
if (compareObservations(previous, current) >= 0) {
|
|
866
|
+
context.addIssue({ code: "custom", message: "association evidence must be duplicate-free and in canonical kind/detail order" });
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
});
|
|
604
870
|
export const zCommitRef = z.object({
|
|
871
|
+
associations: z.array(zSessionAssociation),
|
|
605
872
|
hasSession: z.boolean(),
|
|
606
873
|
hash: z.string(),
|
|
607
874
|
sessionIds: z.array(zSessionID),
|
|
608
875
|
subject: z.string(),
|
|
609
876
|
timeMs: z.int().nullish()
|
|
610
877
|
});
|
|
878
|
+
export const zRewrittenCommit = z.object({
|
|
879
|
+
associations: z.array(zSessionAssociation),
|
|
880
|
+
authorTimeMs: z.int().nullish(),
|
|
881
|
+
confidence: zConfidence,
|
|
882
|
+
ghostHash: z.string(),
|
|
883
|
+
method: zRewriteMethod,
|
|
884
|
+
resolution: zRewriteResolution,
|
|
885
|
+
sessionIds: z.array(zSessionID),
|
|
886
|
+
subject: z.string(),
|
|
887
|
+
successorHash: z.string().nullish()
|
|
888
|
+
});
|
|
611
889
|
export const zSessionIdentity = z.object({
|
|
612
890
|
parentUuid: zSessionID.nullish(),
|
|
613
891
|
schemaVersion: z.int(),
|
|
614
892
|
sessionId: zSessionID
|
|
615
893
|
});
|
|
894
|
+
export const zSessionInsight = z.object({
|
|
895
|
+
classification: zInsightClassification.nullish(),
|
|
896
|
+
confidence: zConfidence,
|
|
897
|
+
evidence: z.array(zInsightEvidence),
|
|
898
|
+
kind: zInsightKind,
|
|
899
|
+
provenance: zInsightProvenance,
|
|
900
|
+
subjects: z.array(z.string()),
|
|
901
|
+
summary: z.string().optional(),
|
|
902
|
+
title: z.string()
|
|
903
|
+
});
|
|
616
904
|
/**
|
|
617
905
|
* Session Outcome
|
|
618
906
|
*
|
|
@@ -742,15 +1030,38 @@ export const zSubagentRef = z.object({
|
|
|
742
1030
|
/**
|
|
743
1031
|
* Target Kind
|
|
744
1032
|
*
|
|
745
|
-
* What is being annotated: session-level, entry-level (turn/tool call), meta-annotation, or
|
|
1033
|
+
* What is being annotated: session-level, entry-level (turn/tool call), meta-annotation, project-level, a specific file version (content-hash keyed read-state receipt), or a durable session-to-commit association
|
|
746
1034
|
*/
|
|
747
1035
|
export const zTargetKind = z.enum([
|
|
748
1036
|
'session',
|
|
749
1037
|
'entry',
|
|
750
1038
|
'annotation',
|
|
751
|
-
'project'
|
|
1039
|
+
'project',
|
|
1040
|
+
'file_version',
|
|
1041
|
+
'association'
|
|
752
1042
|
]);
|
|
753
|
-
export const zAnnotationPushItem = z.
|
|
1043
|
+
export const zAnnotationPushItem = z.intersection(z.union([
|
|
1044
|
+
z.object({
|
|
1045
|
+
targetAssociationId: zAssociationID,
|
|
1046
|
+
targetKind: z.literal('association')
|
|
1047
|
+
}),
|
|
1048
|
+
z.object({
|
|
1049
|
+
sessionId: z.string().min(1),
|
|
1050
|
+
targetKind: z.literal('session')
|
|
1051
|
+
}),
|
|
1052
|
+
z.object({
|
|
1053
|
+
entryTarget: zAnnotationEntryTarget,
|
|
1054
|
+
targetKind: z.literal('entry')
|
|
1055
|
+
}),
|
|
1056
|
+
z.object({
|
|
1057
|
+
annotationId: z.string().min(1),
|
|
1058
|
+
targetKind: z.literal('annotation')
|
|
1059
|
+
}),
|
|
1060
|
+
z.object({
|
|
1061
|
+
projectHash: zProjectHash,
|
|
1062
|
+
targetKind: z.literal('project')
|
|
1063
|
+
})
|
|
1064
|
+
]), z.object({
|
|
754
1065
|
annotationId: z.string().nullish(),
|
|
755
1066
|
annotatorName: z.string().optional(),
|
|
756
1067
|
confidence: z.number().nullish(),
|
|
@@ -761,12 +1072,59 @@ export const zAnnotationPushItem = z.object({
|
|
|
761
1072
|
provenance: zProvenance.nullish(),
|
|
762
1073
|
reason: z.string().nullish(),
|
|
763
1074
|
sessionId: z.string().nullish(),
|
|
1075
|
+
targetAssociationId: zAssociationID.nullish(),
|
|
764
1076
|
targetKind: zTargetKind,
|
|
765
1077
|
typeId: z.string(),
|
|
766
1078
|
value: z.string()
|
|
1079
|
+
})).superRefine((value, context) => {
|
|
1080
|
+
const isPresent = (detail) => detail !== undefined && detail !== null;
|
|
1081
|
+
const hasNonEmptyString = (detail) => typeof detail === "string" && detail !== "";
|
|
1082
|
+
const hasOnly = (allowed) => {
|
|
1083
|
+
const targetFields = [
|
|
1084
|
+
["targetAssociationId", value.targetAssociationId],
|
|
1085
|
+
["sessionId", value.sessionId],
|
|
1086
|
+
["entryTarget", value.entryTarget],
|
|
1087
|
+
["annotationId", value.annotationId],
|
|
1088
|
+
["projectHash", value.projectHash]
|
|
1089
|
+
];
|
|
1090
|
+
return targetFields.every(([name, detail]) => allowed.includes(name) || !isPresent(detail));
|
|
1091
|
+
};
|
|
1092
|
+
const addIssue = (message) => context.addIssue({ code: "custom", message });
|
|
1093
|
+
switch (value.targetKind) {
|
|
1094
|
+
case "association":
|
|
1095
|
+
if (!isPresent(value.targetAssociationId))
|
|
1096
|
+
addIssue("association annotations require targetAssociationId");
|
|
1097
|
+
if (!hasOnly(["targetAssociationId"]))
|
|
1098
|
+
addIssue("association annotations must not mix target arms");
|
|
1099
|
+
return;
|
|
1100
|
+
case "session":
|
|
1101
|
+
if (!hasNonEmptyString(value.sessionId))
|
|
1102
|
+
addIssue("session annotations require a non-empty sessionId");
|
|
1103
|
+
if (!hasOnly(["sessionId"]))
|
|
1104
|
+
addIssue("session annotations must not mix target arms");
|
|
1105
|
+
return;
|
|
1106
|
+
case "entry":
|
|
1107
|
+
if (!isPresent(value.entryTarget))
|
|
1108
|
+
addIssue("entry annotations require entryTarget");
|
|
1109
|
+
if (!hasOnly(["entryTarget"]))
|
|
1110
|
+
addIssue("entry annotations must not mix target arms");
|
|
1111
|
+
return;
|
|
1112
|
+
case "annotation":
|
|
1113
|
+
if (!hasNonEmptyString(value.annotationId))
|
|
1114
|
+
addIssue("annotation targets require a non-empty annotationId");
|
|
1115
|
+
if (!hasOnly(["annotationId"]))
|
|
1116
|
+
addIssue("annotation targets must not mix target arms");
|
|
1117
|
+
return;
|
|
1118
|
+
case "project":
|
|
1119
|
+
if (!isPresent(value.projectHash))
|
|
1120
|
+
addIssue("project annotations require projectHash");
|
|
1121
|
+
if (!hasOnly(["projectHash"]))
|
|
1122
|
+
addIssue("project annotations must not mix target arms");
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
767
1125
|
});
|
|
768
1126
|
export const zAnnotationPushRequest = z.object({
|
|
769
|
-
annotations: z.array(zAnnotationPushItem)
|
|
1127
|
+
annotations: z.array(zAnnotationPushItem),
|
|
770
1128
|
retractions: z.array(z.string()).optional()
|
|
771
1129
|
});
|
|
772
1130
|
export const zAnnotationSummary = z.object({
|
|
@@ -781,14 +1139,74 @@ export const zAnnotationSummary = z.object({
|
|
|
781
1139
|
reason: z.string().nullish(),
|
|
782
1140
|
supersededBy: z.string().nullish(),
|
|
783
1141
|
targetAnnotationId: z.string().nullish(),
|
|
1142
|
+
targetAssociationId: zAssociationID.nullish(),
|
|
1143
|
+
targetContentHash: z.string().nullish(),
|
|
784
1144
|
targetEntryEndIndex: z.int().nullish(),
|
|
785
1145
|
targetEntryIndex: z.int().nullish(),
|
|
1146
|
+
targetFilePath: z.string().nullish(),
|
|
786
1147
|
targetKind: zTargetKind,
|
|
787
1148
|
targetProjectHash: zProjectHash.nullish(),
|
|
788
1149
|
targetSessionId: z.string().nullish(),
|
|
789
1150
|
typeId: z.string(),
|
|
790
1151
|
typeName: z.string(),
|
|
791
1152
|
value: z.string()
|
|
1153
|
+
}).superRefine((value, context) => {
|
|
1154
|
+
const isPresent = (detail) => detail !== undefined && detail !== null;
|
|
1155
|
+
const hasNonEmptyString = (detail) => typeof detail === "string" && detail !== "";
|
|
1156
|
+
const hasOnly = (allowed) => {
|
|
1157
|
+
const targetFields = [
|
|
1158
|
+
["targetAssociationId", value.targetAssociationId],
|
|
1159
|
+
["targetSessionId", value.targetSessionId],
|
|
1160
|
+
["targetEntryIndex", value.targetEntryIndex],
|
|
1161
|
+
["targetEntryEndIndex", value.targetEntryEndIndex],
|
|
1162
|
+
["targetAnnotationId", value.targetAnnotationId],
|
|
1163
|
+
["targetProjectHash", value.targetProjectHash],
|
|
1164
|
+
["targetFilePath", value.targetFilePath],
|
|
1165
|
+
["targetContentHash", value.targetContentHash]
|
|
1166
|
+
];
|
|
1167
|
+
return targetFields.every(([name, detail]) => allowed.includes(name) || !isPresent(detail));
|
|
1168
|
+
};
|
|
1169
|
+
const addIssue = (message) => context.addIssue({ code: "custom", message });
|
|
1170
|
+
switch (value.targetKind) {
|
|
1171
|
+
case "association":
|
|
1172
|
+
if (!isPresent(value.targetAssociationId))
|
|
1173
|
+
addIssue("association annotations require targetAssociationId");
|
|
1174
|
+
if (!hasOnly(["targetAssociationId"]))
|
|
1175
|
+
addIssue("association annotations must not mix target arms");
|
|
1176
|
+
return;
|
|
1177
|
+
case "session":
|
|
1178
|
+
if (!hasNonEmptyString(value.targetSessionId))
|
|
1179
|
+
addIssue("session annotations require a non-empty targetSessionId");
|
|
1180
|
+
if (!hasOnly(["targetSessionId"]))
|
|
1181
|
+
addIssue("session annotations must not mix target arms");
|
|
1182
|
+
return;
|
|
1183
|
+
case "entry":
|
|
1184
|
+
if (!hasNonEmptyString(value.targetSessionId) || !isPresent(value.targetEntryIndex))
|
|
1185
|
+
addIssue("entry annotations require targetSessionId and targetEntryIndex");
|
|
1186
|
+
if (!hasOnly(["targetSessionId", "targetEntryIndex", "targetEntryEndIndex"]))
|
|
1187
|
+
addIssue("entry annotations must not mix target arms");
|
|
1188
|
+
if (value.targetEntryIndex !== undefined && value.targetEntryIndex !== null && value.targetEntryEndIndex !== undefined && value.targetEntryEndIndex !== null && value.targetEntryEndIndex <= value.targetEntryIndex)
|
|
1189
|
+
addIssue("entry annotation targetEntryEndIndex must be greater than targetEntryIndex");
|
|
1190
|
+
return;
|
|
1191
|
+
case "annotation":
|
|
1192
|
+
if (!hasNonEmptyString(value.targetAnnotationId))
|
|
1193
|
+
addIssue("annotation targets require a non-empty targetAnnotationId");
|
|
1194
|
+
if (!hasOnly(["targetAnnotationId"]))
|
|
1195
|
+
addIssue("annotation targets must not mix target arms");
|
|
1196
|
+
return;
|
|
1197
|
+
case "project":
|
|
1198
|
+
if (!isPresent(value.targetProjectHash))
|
|
1199
|
+
addIssue("project annotations require targetProjectHash");
|
|
1200
|
+
if (!hasOnly(["targetProjectHash"]))
|
|
1201
|
+
addIssue("project annotations must not mix target arms");
|
|
1202
|
+
return;
|
|
1203
|
+
case "file_version":
|
|
1204
|
+
if (!hasNonEmptyString(value.targetFilePath) || !hasNonEmptyString(value.targetContentHash))
|
|
1205
|
+
addIssue("file_version annotations require non-empty targetFilePath and targetContentHash");
|
|
1206
|
+
if (!hasOnly(["targetFilePath", "targetContentHash"]))
|
|
1207
|
+
addIssue("file_version annotations must not mix target arms");
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
792
1210
|
});
|
|
793
1211
|
export const zAnnotationsPayload = z.object({
|
|
794
1212
|
annotations: z.array(zAnnotationSummary).nullable(),
|
|
@@ -809,8 +1227,11 @@ export const zPullAnnotation = z.object({
|
|
|
809
1227
|
reason: z.string().nullish(),
|
|
810
1228
|
supersededBy: z.string().nullish(),
|
|
811
1229
|
targetAnnotationId: z.string().nullish(),
|
|
1230
|
+
targetAssociationId: zAssociationID.optional(),
|
|
1231
|
+
targetContentHash: z.string().nullish(),
|
|
812
1232
|
targetEntryEndIndex: z.int().nullish(),
|
|
813
1233
|
targetEntryIndex: z.int().nullish(),
|
|
1234
|
+
targetFilePath: z.string().nullish(),
|
|
814
1235
|
targetKind: zTargetKind.optional(),
|
|
815
1236
|
targetProjectHash: zProjectHash.optional(),
|
|
816
1237
|
targetSessionId: z.string().nullish(),
|
|
@@ -852,6 +1273,7 @@ export const zTaskSummary = z.object({
|
|
|
852
1273
|
labels: z.array(z.string()).nullable(),
|
|
853
1274
|
outcome: z.string().optional(),
|
|
854
1275
|
readCount: z.int(),
|
|
1276
|
+
readFiles: z.array(z.string()),
|
|
855
1277
|
retryLoop: z.boolean(),
|
|
856
1278
|
sessionId: z.string(),
|
|
857
1279
|
startMs: z.int().nullish(),
|
|
@@ -868,6 +1290,7 @@ export const zChangeSession = z.object({
|
|
|
868
1290
|
export const zMapNodeDetailPayload = z.object({
|
|
869
1291
|
costUsd: z.number().nullish(),
|
|
870
1292
|
dependsOn: z.array(z.string()).nullable(),
|
|
1293
|
+
insights: z.array(zSessionInsight),
|
|
871
1294
|
kind: zMapNodeKind,
|
|
872
1295
|
language: z.string().optional(),
|
|
873
1296
|
lastTouchMs: z.int().nullish(),
|
|
@@ -877,6 +1300,7 @@ export const zMapNodeDetailPayload = z.object({
|
|
|
877
1300
|
recentCommits: z.array(zCommitRef).nullable(),
|
|
878
1301
|
recordedFiles: z.int(),
|
|
879
1302
|
retryLoops: z.int(),
|
|
1303
|
+
rewrittenCommits: z.array(zRewrittenCommit),
|
|
880
1304
|
sessionCount: z.int(),
|
|
881
1305
|
shapedBy: z.array(zTaskSummary).nullable(),
|
|
882
1306
|
taskCount: z.int(),
|
|
@@ -901,6 +1325,7 @@ export const zReviewListPayload = z.object({
|
|
|
901
1325
|
projectHash: zProjectHash,
|
|
902
1326
|
recentCommits: z.array(zCommitRef),
|
|
903
1327
|
repoFound: z.boolean(),
|
|
1328
|
+
rewrittenCommits: z.array(zRewrittenCommit),
|
|
904
1329
|
sessions: z.array(zTimelineSessionRef)
|
|
905
1330
|
});
|
|
906
1331
|
export const zTimestampInfo = z.object({
|
|
@@ -1091,6 +1516,7 @@ export const zChangeDetailPayload = z.object({
|
|
|
1091
1516
|
defaultBranch: z.string(),
|
|
1092
1517
|
files: z.array(zFileChange).nullable(),
|
|
1093
1518
|
frictions: z.array(zFrictionCluster).nullable(),
|
|
1519
|
+
insights: z.array(zSessionInsight),
|
|
1094
1520
|
linesAdded: z.int(),
|
|
1095
1521
|
linesRemoved: z.int(),
|
|
1096
1522
|
newEdges: z.array(zMapEdge).nullable(),
|