@oscharko-dev/keiko-contracts 0.2.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.
- package/dist/.tsbuildinfo +1 -0
- package/dist/bff-wire.d.ts +661 -0
- package/dist/bff-wire.d.ts.map +1 -0
- package/dist/bff-wire.js +102 -0
- package/dist/bug-investigation-events.d.ts +92 -0
- package/dist/bug-investigation-events.d.ts.map +1 -0
- package/dist/bug-investigation-events.js +18 -0
- package/dist/coding-context.d.ts +76 -0
- package/dist/coding-context.d.ts.map +1 -0
- package/dist/coding-context.js +158 -0
- package/dist/connected-context.d.ts +174 -0
- package/dist/connected-context.d.ts.map +1 -0
- package/dist/connected-context.js +636 -0
- package/dist/conversation-budget.d.ts +37 -0
- package/dist/conversation-budget.d.ts.map +1 -0
- package/dist/conversation-budget.js +97 -0
- package/dist/editor-agent.d.ts +131 -0
- package/dist/editor-agent.d.ts.map +1 -0
- package/dist/editor-agent.js +197 -0
- package/dist/editor-completion.d.ts +62 -0
- package/dist/editor-completion.d.ts.map +1 -0
- package/dist/editor-completion.js +147 -0
- package/dist/editor-dirty-close.d.ts +17 -0
- package/dist/editor-dirty-close.d.ts.map +1 -0
- package/dist/editor-dirty-close.js +8 -0
- package/dist/editor-hot-exit.d.ts +18 -0
- package/dist/editor-hot-exit.d.ts.map +1 -0
- package/dist/editor-hot-exit.js +42 -0
- package/dist/editor-inline-completion.d.ts +70 -0
- package/dist/editor-inline-completion.d.ts.map +1 -0
- package/dist/editor-inline-completion.js +215 -0
- package/dist/editor-layout.d.ts +105 -0
- package/dist/editor-layout.d.ts.map +1 -0
- package/dist/editor-layout.js +479 -0
- package/dist/editor-patch-apply.d.ts +77 -0
- package/dist/editor-patch-apply.d.ts.map +1 -0
- package/dist/editor-patch-apply.js +122 -0
- package/dist/editor-session.d.ts +31 -0
- package/dist/editor-session.d.ts.map +1 -0
- package/dist/editor-session.js +75 -0
- package/dist/editor-test-generation.d.ts +104 -0
- package/dist/editor-test-generation.d.ts.map +1 -0
- package/dist/editor-test-generation.js +211 -0
- package/dist/evaluations.d.ts +75 -0
- package/dist/evaluations.d.ts.map +1 -0
- package/dist/evaluations.js +16 -0
- package/dist/evidence.d.ts +297 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +9 -0
- package/dist/gateway.d.ts +129 -0
- package/dist/gateway.d.ts.map +1 -0
- package/dist/gateway.js +66 -0
- package/dist/harness.d.ts +274 -0
- package/dist/harness.d.ts.map +1 -0
- package/dist/harness.js +38 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +83 -0
- package/dist/language-service.d.ts +145 -0
- package/dist/language-service.d.ts.map +1 -0
- package/dist/language-service.js +161 -0
- package/dist/local-knowledge-large-document-validation.d.ts +7 -0
- package/dist/local-knowledge-large-document-validation.d.ts.map +1 -0
- package/dist/local-knowledge-large-document-validation.js +161 -0
- package/dist/local-knowledge-large-document.d.ts +113 -0
- package/dist/local-knowledge-large-document.d.ts.map +1 -0
- package/dist/local-knowledge-large-document.js +142 -0
- package/dist/local-knowledge-paths.d.ts +3 -0
- package/dist/local-knowledge-paths.d.ts.map +1 -0
- package/dist/local-knowledge-paths.js +65 -0
- package/dist/local-knowledge-records.d.ts +190 -0
- package/dist/local-knowledge-records.d.ts.map +1 -0
- package/dist/local-knowledge-records.js +36 -0
- package/dist/local-knowledge-schema-validation.d.ts +19 -0
- package/dist/local-knowledge-schema-validation.d.ts.map +1 -0
- package/dist/local-knowledge-schema-validation.js +115 -0
- package/dist/local-knowledge-schema.d.ts +14 -0
- package/dist/local-knowledge-schema.d.ts.map +1 -0
- package/dist/local-knowledge-schema.js +715 -0
- package/dist/local-knowledge-validation.d.ts +20 -0
- package/dist/local-knowledge-validation.d.ts.map +1 -0
- package/dist/local-knowledge-validation.js +487 -0
- package/dist/local-knowledge.d.ts +158 -0
- package/dist/local-knowledge.d.ts.map +1 -0
- package/dist/local-knowledge.js +63 -0
- package/dist/memory-audit-events.d.ts +73 -0
- package/dist/memory-audit-events.d.ts.map +1 -0
- package/dist/memory-audit-events.js +44 -0
- package/dist/memory-audit-validation.d.ts +4 -0
- package/dist/memory-audit-validation.d.ts.map +1 -0
- package/dist/memory-audit-validation.js +151 -0
- package/dist/memory-barrel.d.ts +15 -0
- package/dist/memory-barrel.d.ts.map +1 -0
- package/dist/memory-barrel.js +20 -0
- package/dist/memory-internal.d.ts +26 -0
- package/dist/memory-internal.d.ts.map +1 -0
- package/dist/memory-internal.js +104 -0
- package/dist/memory-operations-validation.d.ts +12 -0
- package/dist/memory-operations-validation.d.ts.map +1 -0
- package/dist/memory-operations-validation.js +267 -0
- package/dist/memory-operations.d.ts +156 -0
- package/dist/memory-operations.d.ts.map +1 -0
- package/dist/memory-operations.js +29 -0
- package/dist/memory-record-validation.d.ts +10 -0
- package/dist/memory-record-validation.d.ts.map +1 -0
- package/dist/memory-record-validation.js +101 -0
- package/dist/memory-records.d.ts +66 -0
- package/dist/memory-records.d.ts.map +1 -0
- package/dist/memory-records.js +22 -0
- package/dist/memory-retrieval-validation.d.ts +6 -0
- package/dist/memory-retrieval-validation.d.ts.map +1 -0
- package/dist/memory-retrieval-validation.js +108 -0
- package/dist/memory-validation.d.ts +31 -0
- package/dist/memory-validation.d.ts.map +1 -0
- package/dist/memory-validation.js +318 -0
- package/dist/memory-workflow-port.d.ts +26 -0
- package/dist/memory-workflow-port.d.ts.map +1 -0
- package/dist/memory-workflow-port.js +13 -0
- package/dist/memory.d.ts +81 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +104 -0
- package/dist/prompt-enhancer-analyzer.d.ts +7 -0
- package/dist/prompt-enhancer-analyzer.d.ts.map +1 -0
- package/dist/prompt-enhancer-analyzer.js +745 -0
- package/dist/prompt-enhancer-bff.d.ts +67 -0
- package/dist/prompt-enhancer-bff.d.ts.map +1 -0
- package/dist/prompt-enhancer-bff.js +156 -0
- package/dist/prompt-enhancer-critic.d.ts +46 -0
- package/dist/prompt-enhancer-critic.d.ts.map +1 -0
- package/dist/prompt-enhancer-critic.js +35 -0
- package/dist/prompt-enhancer-grounding.d.ts +19 -0
- package/dist/prompt-enhancer-grounding.d.ts.map +1 -0
- package/dist/prompt-enhancer-grounding.js +235 -0
- package/dist/prompt-enhancer-safety.d.ts +66 -0
- package/dist/prompt-enhancer-safety.d.ts.map +1 -0
- package/dist/prompt-enhancer-safety.js +446 -0
- package/dist/prompt-enhancer-validation.d.ts +28 -0
- package/dist/prompt-enhancer-validation.d.ts.map +1 -0
- package/dist/prompt-enhancer-validation.js +931 -0
- package/dist/prompt-enhancer.d.ts +184 -0
- package/dist/prompt-enhancer.d.ts.map +1 -0
- package/dist/prompt-enhancer.js +350 -0
- package/dist/qualityIntelligence/assertNever.d.ts +2 -0
- package/dist/qualityIntelligence/assertNever.d.ts.map +1 -0
- package/dist/qualityIntelligence/assertNever.js +7 -0
- package/dist/qualityIntelligence/auditSummary.d.ts +25 -0
- package/dist/qualityIntelligence/auditSummary.d.ts.map +1 -0
- package/dist/qualityIntelligence/auditSummary.js +7 -0
- package/dist/qualityIntelligence/bffWire.d.ts +356 -0
- package/dist/qualityIntelligence/bffWire.d.ts.map +1 -0
- package/dist/qualityIntelligence/bffWire.js +22 -0
- package/dist/qualityIntelligence/coverageMap.d.ts +21 -0
- package/dist/qualityIntelligence/coverageMap.d.ts.map +1 -0
- package/dist/qualityIntelligence/coverageMap.js +29 -0
- package/dist/qualityIntelligence/editableRevision.d.ts +21 -0
- package/dist/qualityIntelligence/editableRevision.d.ts.map +1 -0
- package/dist/qualityIntelligence/editableRevision.js +8 -0
- package/dist/qualityIntelligence/evidenceAtom.d.ts +35 -0
- package/dist/qualityIntelligence/evidenceAtom.d.ts.map +1 -0
- package/dist/qualityIntelligence/evidenceAtom.js +29 -0
- package/dist/qualityIntelligence/exportBundle.d.ts +28 -0
- package/dist/qualityIntelligence/exportBundle.d.ts.map +1 -0
- package/dist/qualityIntelligence/exportBundle.js +46 -0
- package/dist/qualityIntelligence/handoffEnvelope.d.ts +23 -0
- package/dist/qualityIntelligence/handoffEnvelope.d.ts.map +1 -0
- package/dist/qualityIntelligence/handoffEnvelope.js +8 -0
- package/dist/qualityIntelligence/ids.d.ts +58 -0
- package/dist/qualityIntelligence/ids.d.ts.map +1 -0
- package/dist/qualityIntelligence/ids.js +93 -0
- package/dist/qualityIntelligence/index.d.ts +29 -0
- package/dist/qualityIntelligence/index.d.ts.map +1 -0
- package/dist/qualityIntelligence/index.js +20 -0
- package/dist/qualityIntelligence/reviewRecord.d.ts +19 -0
- package/dist/qualityIntelligence/reviewRecord.d.ts.map +1 -0
- package/dist/qualityIntelligence/reviewRecord.js +20 -0
- package/dist/qualityIntelligence/runPlanAndEvents.d.ts +84 -0
- package/dist/qualityIntelligence/runPlanAndEvents.d.ts.map +1 -0
- package/dist/qualityIntelligence/runPlanAndEvents.js +51 -0
- package/dist/qualityIntelligence/sourceEnvelope.d.ts +77 -0
- package/dist/qualityIntelligence/sourceEnvelope.d.ts.map +1 -0
- package/dist/qualityIntelligence/sourceEnvelope.js +118 -0
- package/dist/qualityIntelligence/testCaseCandidate.d.ts +21 -0
- package/dist/qualityIntelligence/testCaseCandidate.d.ts.map +1 -0
- package/dist/qualityIntelligence/testCaseCandidate.js +21 -0
- package/dist/qualityIntelligence/testQualityRubric.d.ts +17 -0
- package/dist/qualityIntelligence/testQualityRubric.d.ts.map +1 -0
- package/dist/qualityIntelligence/testQualityRubric.js +32 -0
- package/dist/qualityIntelligence/validationFinding.d.ts +48 -0
- package/dist/qualityIntelligence/validationFinding.d.ts.map +1 -0
- package/dist/qualityIntelligence/validationFinding.js +36 -0
- package/dist/relationships-validation.d.ts +13 -0
- package/dist/relationships-validation.d.ts.map +1 -0
- package/dist/relationships-validation.js +422 -0
- package/dist/relationships.d.ts +79 -0
- package/dist/relationships.d.ts.map +1 -0
- package/dist/relationships.js +307 -0
- package/dist/text-safety.d.ts +7 -0
- package/dist/text-safety.d.ts.map +1 -0
- package/dist/text-safety.js +58 -0
- package/dist/tools.d.ts +153 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +118 -0
- package/dist/unit-test-events.d.ts +87 -0
- package/dist/unit-test-events.d.ts.map +1 -0
- package/dist/unit-test-events.js +14 -0
- package/dist/verification-summary.d.ts +38 -0
- package/dist/verification-summary.d.ts.map +1 -0
- package/dist/verification-summary.js +5 -0
- package/dist/verification.d.ts +64 -0
- package/dist/verification.d.ts.map +1 -0
- package/dist/verification.js +13 -0
- package/dist/workflow-descriptor.d.ts +21 -0
- package/dist/workflow-descriptor.d.ts.map +1 -0
- package/dist/workflow-descriptor.js +8 -0
- package/dist/workflow-handoff.d.ts +69 -0
- package/dist/workflow-handoff.d.ts.map +1 -0
- package/dist/workflow-handoff.js +381 -0
- package/dist/workspace-descriptors.d.ts +21 -0
- package/dist/workspace-descriptors.d.ts.map +1 -0
- package/dist/workspace-descriptors.js +180 -0
- package/dist/workspace-ui.d.ts +119 -0
- package/dist/workspace-ui.d.ts.map +1 -0
- package/dist/workspace-ui.js +105 -0
- package/dist/workspace.d.ts +104 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +27 -0
- package/package.json +71 -0
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
// Public type contracts for the connected repository context surface (Epic #177, Issue #178).
|
|
2
|
+
// All string content carried by these types is already redacted upstream (keiko-security policy);
|
|
3
|
+
// nothing here performs redaction, IO, hashing, clock reads, or randomness. Pure data + pure
|
|
4
|
+
// validators only. The schemaVersion discriminant follows the same evolution rule as
|
|
5
|
+
// EVIDENCE_SCHEMA_VERSION (ADR-0010 D2): a breaking change introduces a NEW literal member
|
|
6
|
+
// rather than mutating "1". Leaf-package rule (ADR-0019 direction 1) means no
|
|
7
|
+
// `@oscharko-dev/keiko-*` imports may appear in this module.
|
|
8
|
+
// ─── Schema version ───────────────────────────────────────────────────────────
|
|
9
|
+
export const CONNECTED_CONTEXT_SCHEMA_VERSION = "1";
|
|
10
|
+
export const SELECTED_SCOPE_KINDS = [
|
|
11
|
+
"workspace-root",
|
|
12
|
+
"directory",
|
|
13
|
+
"files",
|
|
14
|
+
];
|
|
15
|
+
export const EVIDENCE_ATOM_PROVENANCE_KINDS = [
|
|
16
|
+
"lexical-search",
|
|
17
|
+
"file-listing",
|
|
18
|
+
"excerpt-read",
|
|
19
|
+
"structural",
|
|
20
|
+
"git-history",
|
|
21
|
+
"model-rerank",
|
|
22
|
+
"document-extract",
|
|
23
|
+
];
|
|
24
|
+
export const EVIDENCE_ATOM_REDACTION_STATES = [
|
|
25
|
+
"redacted",
|
|
26
|
+
"raw-internal",
|
|
27
|
+
];
|
|
28
|
+
export const DEFAULT_EXPLORATION_BUDGET = {
|
|
29
|
+
searchCallsMax: 16,
|
|
30
|
+
filesReadMax: 32,
|
|
31
|
+
excerptBytesMax: 131_072,
|
|
32
|
+
modelInputTokensMax: 32_000,
|
|
33
|
+
modelOutputTokensMax: 4_096,
|
|
34
|
+
elapsedMsMax: 30_000,
|
|
35
|
+
rerankCallsMax: 0,
|
|
36
|
+
};
|
|
37
|
+
export const RETRIEVAL_QUERY_KINDS = [
|
|
38
|
+
"natural-language",
|
|
39
|
+
"exact-symbol",
|
|
40
|
+
"file-pattern",
|
|
41
|
+
"regex",
|
|
42
|
+
];
|
|
43
|
+
export const CANDIDATE_OMISSION_REASONS = [
|
|
44
|
+
"outside-scope",
|
|
45
|
+
"binary",
|
|
46
|
+
"generated",
|
|
47
|
+
"ignored",
|
|
48
|
+
"size-exceeded",
|
|
49
|
+
"near-duplicate",
|
|
50
|
+
"low-relevance",
|
|
51
|
+
"redacted-only",
|
|
52
|
+
"budget-exhausted",
|
|
53
|
+
"tool-unavailable",
|
|
54
|
+
"unsupported-format",
|
|
55
|
+
"no-text-layer",
|
|
56
|
+
"malformed-document",
|
|
57
|
+
"encrypted-document",
|
|
58
|
+
];
|
|
59
|
+
export const CONNECTED_FILE_ROLES = [
|
|
60
|
+
"read-only",
|
|
61
|
+
"editable",
|
|
62
|
+
];
|
|
63
|
+
export const UNCERTAINTY_MARKER_KINDS = [
|
|
64
|
+
"no-evidence",
|
|
65
|
+
"stale-evidence",
|
|
66
|
+
"scope-incomplete",
|
|
67
|
+
"budget-clipped",
|
|
68
|
+
"tool-unavailable",
|
|
69
|
+
"low-confidence",
|
|
70
|
+
];
|
|
71
|
+
// Module-scope regex (avoid per-call allocation; safe — no backtracking risk).
|
|
72
|
+
const WINDOWS_DRIVE_RE = /^[A-Za-z]:/;
|
|
73
|
+
const WINDOWS_DRIVE_ABSOLUTE_RE = /^[A-Za-z]:[\\/]/;
|
|
74
|
+
const WINDOWS_DEVICE_PREFIX_RE = /^[\\/]{2}[?.][\\/]/;
|
|
75
|
+
const WINDOWS_UNC_PREFIX_RE = /^[\\/]{2}[^\\/?.]/;
|
|
76
|
+
const REMOTE_URL_PREFIX_RE = /^[a-zA-Z][a-zA-Z0-9+\-.]*:\/\//;
|
|
77
|
+
const TRAVERSAL_SEGMENT_RE = /(^|[\\/])(?:\.{1,2})(?:[\\/]|$)/;
|
|
78
|
+
// The schema discriminant comparisons below are statically true at the type level (the
|
|
79
|
+
// constant equals the literal field type), so we widen to `string` before comparing. This
|
|
80
|
+
// keeps the validator honest against runtime inputs that bypass the type system — e.g.,
|
|
81
|
+
// objects materialized from JSON.parse or cross-version manifests.
|
|
82
|
+
function schemaMismatch(actual, expected) {
|
|
83
|
+
return actual !== expected;
|
|
84
|
+
}
|
|
85
|
+
function isRecord(value) {
|
|
86
|
+
return typeof value === "object" && value !== null;
|
|
87
|
+
}
|
|
88
|
+
function isFiniteNonNegativeInteger(value) {
|
|
89
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
90
|
+
}
|
|
91
|
+
function isFinitePositiveInteger(value) {
|
|
92
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
93
|
+
}
|
|
94
|
+
function isNonEmptyTrimmed(value) {
|
|
95
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
96
|
+
}
|
|
97
|
+
function hasInvalidPathPrefix(path) {
|
|
98
|
+
if (path.startsWith("/")) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
if (path.startsWith("\\\\")) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
return WINDOWS_DRIVE_RE.test(path);
|
|
105
|
+
}
|
|
106
|
+
function hasInvalidSegments(path) {
|
|
107
|
+
const segments = path.split("/");
|
|
108
|
+
for (const segment of segments) {
|
|
109
|
+
if (segment.length === 0) {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
if (segment === "." || segment === "..") {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
export function isValidScopePath(path, options) {
|
|
119
|
+
if (typeof path !== "string") {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
if (!options.mustBeRelative) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
if (path.length === 0) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
if (path.includes("\0")) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
// Backslashes are not valid path separators in POSIX workspace-relative paths; reject
|
|
132
|
+
// them before segment analysis so Windows-style traversals cannot slip through.
|
|
133
|
+
if (path.includes("\\")) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
if (hasInvalidPathPrefix(path)) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
return !hasInvalidSegments(path);
|
|
140
|
+
}
|
|
141
|
+
function isValidWorkspaceRootPath(path) {
|
|
142
|
+
if (typeof path !== "string") {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
if (!isNonEmptyTrimmed(path)) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
if (path.includes("\0")) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
if (WINDOWS_DEVICE_PREFIX_RE.test(path) || WINDOWS_UNC_PREFIX_RE.test(path)) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
if (!WINDOWS_DRIVE_ABSOLUTE_RE.test(path) && REMOTE_URL_PREFIX_RE.test(path)) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
if (!path.startsWith("/") && !WINDOWS_DRIVE_ABSOLUTE_RE.test(path)) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
return !TRAVERSAL_SEGMENT_RE.test(path);
|
|
161
|
+
}
|
|
162
|
+
export function isValidLineRange(range) {
|
|
163
|
+
if (!isRecord(range)) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
if (!Number.isInteger(range.startLine) || !Number.isInteger(range.endLine)) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
if (range.startLine < 1) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
return range.endLine >= range.startLine;
|
|
173
|
+
}
|
|
174
|
+
export function isWithinBudget(usage, budget) {
|
|
175
|
+
if (!isRecord(usage) || !isRecord(budget)) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
const dims = [
|
|
179
|
+
[usage.searchCalls, budget.searchCallsMax],
|
|
180
|
+
[usage.filesRead, budget.filesReadMax],
|
|
181
|
+
[usage.excerptBytes, budget.excerptBytesMax],
|
|
182
|
+
[usage.modelInputTokens, budget.modelInputTokensMax],
|
|
183
|
+
[usage.modelOutputTokens, budget.modelOutputTokensMax],
|
|
184
|
+
[usage.elapsedMs, budget.elapsedMsMax],
|
|
185
|
+
[usage.rerankCalls, budget.rerankCallsMax],
|
|
186
|
+
];
|
|
187
|
+
for (const dim of dims) {
|
|
188
|
+
const used = dim[0];
|
|
189
|
+
const cap = dim[1];
|
|
190
|
+
if (!Number.isFinite(used) || used < 0) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
if (used > cap) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
function pushIf(reasons, condition, reason) {
|
|
200
|
+
if (condition) {
|
|
201
|
+
reasons.push(reason);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function buildResult(reasons) {
|
|
205
|
+
return reasons.length === 0 ? { ok: true } : { ok: false, reasons };
|
|
206
|
+
}
|
|
207
|
+
function hasStringArray(value) {
|
|
208
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
209
|
+
}
|
|
210
|
+
function appendPrefixedReasons(result, prefix, reasons) {
|
|
211
|
+
if (result.ok) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
for (const reason of result.reasons) {
|
|
215
|
+
reasons.push(`${prefix}${reason}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function isRuntimeSelectedScope(value) {
|
|
219
|
+
if (!isRecord(value)) {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
if (!SELECTED_SCOPE_KINDS.includes(value.kind)) {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
return hasStringArray(value.relativePaths);
|
|
226
|
+
}
|
|
227
|
+
function isCandidateOmissionReason(value) {
|
|
228
|
+
return (typeof value === "string" &&
|
|
229
|
+
CANDIDATE_OMISSION_REASONS.includes(value));
|
|
230
|
+
}
|
|
231
|
+
function isConnectedFileRole(value) {
|
|
232
|
+
return typeof value === "string" && CONNECTED_FILE_ROLES.includes(value);
|
|
233
|
+
}
|
|
234
|
+
function isUncertaintyMarkerKind(value) {
|
|
235
|
+
return (typeof value === "string" && UNCERTAINTY_MARKER_KINDS.includes(value));
|
|
236
|
+
}
|
|
237
|
+
function validateScopeKindPaths(scope, reasons) {
|
|
238
|
+
const paths = scope.relativePaths;
|
|
239
|
+
if (scope.kind === "workspace-root" && paths.length !== 0) {
|
|
240
|
+
reasons.push("scope.relativePaths must be empty for workspace-root");
|
|
241
|
+
}
|
|
242
|
+
if (scope.kind === "directory" && paths.length !== 1) {
|
|
243
|
+
reasons.push("scope.relativePaths must have exactly one entry for directory");
|
|
244
|
+
}
|
|
245
|
+
if (scope.kind === "files" && paths.length < 1) {
|
|
246
|
+
reasons.push("scope.relativePaths must be non-empty for files");
|
|
247
|
+
}
|
|
248
|
+
for (const candidate of paths) {
|
|
249
|
+
if (!isValidScopePath(candidate, { mustBeRelative: true })) {
|
|
250
|
+
reasons.push("scope.relativePaths contains an invalid path");
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function isPathWithinSelectedScope(scope, candidatePath) {
|
|
256
|
+
if (typeof candidatePath !== "string") {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
if (scope.kind === "workspace-root") {
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
return scope.relativePaths.some((scopePath) => candidatePath === scopePath || candidatePath.startsWith(`${scopePath}/`));
|
|
263
|
+
}
|
|
264
|
+
function pathsOverlap(a, b) {
|
|
265
|
+
return a === b || a.startsWith(`${b}/`) || b.startsWith(`${a}/`);
|
|
266
|
+
}
|
|
267
|
+
function setHasOverlappingPath(paths, candidatePath) {
|
|
268
|
+
for (const path of paths) {
|
|
269
|
+
if (pathsOverlap(path, candidatePath)) {
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
export function validateSelectedScope(scope) {
|
|
276
|
+
const reasons = [];
|
|
277
|
+
if (!isRecord(scope)) {
|
|
278
|
+
return buildResult(["scope invalid"]);
|
|
279
|
+
}
|
|
280
|
+
pushIf(reasons, schemaMismatch(scope.schemaVersion, CONNECTED_CONTEXT_SCHEMA_VERSION), "scope.schemaVersion mismatch");
|
|
281
|
+
pushIf(reasons, !isNonEmptyTrimmed(scope.scopeId), "scope.scopeId empty");
|
|
282
|
+
pushIf(reasons, !isNonEmptyTrimmed(scope.workspaceRoot), "scope.workspaceRoot empty");
|
|
283
|
+
if (isNonEmptyTrimmed(scope.workspaceRoot)) {
|
|
284
|
+
pushIf(reasons, !isValidWorkspaceRootPath(scope.workspaceRoot), "scope.workspaceRoot invalid");
|
|
285
|
+
}
|
|
286
|
+
pushIf(reasons, !SELECTED_SCOPE_KINDS.includes(scope.kind), "scope.kind invalid");
|
|
287
|
+
if (!Array.isArray(scope.relativePaths)) {
|
|
288
|
+
reasons.push("scope.relativePaths invalid");
|
|
289
|
+
}
|
|
290
|
+
else if (!hasStringArray(scope.relativePaths)) {
|
|
291
|
+
reasons.push("scope.relativePaths contains an invalid path");
|
|
292
|
+
}
|
|
293
|
+
else if (SELECTED_SCOPE_KINDS.includes(scope.kind)) {
|
|
294
|
+
validateScopeKindPaths(scope, reasons);
|
|
295
|
+
}
|
|
296
|
+
pushIf(reasons, !isFiniteNonNegativeInteger(scope.connectedAtMs), "scope.connectedAtMs invalid");
|
|
297
|
+
if (scope.conversationId !== undefined) {
|
|
298
|
+
pushIf(reasons, !isNonEmptyTrimmed(scope.conversationId), "scope.conversationId empty");
|
|
299
|
+
}
|
|
300
|
+
return buildResult(reasons);
|
|
301
|
+
}
|
|
302
|
+
function validateLedgerRef(ref, reasons, prefix) {
|
|
303
|
+
if (!isRecord(ref)) {
|
|
304
|
+
reasons.push(`${prefix}.ledgerRef invalid`);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
if (schemaMismatch(ref.evidenceSchemaVersion, "1")) {
|
|
308
|
+
reasons.push(`${prefix}.ledgerRef.evidenceSchemaVersion mismatch`);
|
|
309
|
+
}
|
|
310
|
+
if (!isNonEmptyTrimmed(ref.runId)) {
|
|
311
|
+
reasons.push(`${prefix}.ledgerRef.runId empty`);
|
|
312
|
+
}
|
|
313
|
+
if (ref.atomId !== undefined && !isNonEmptyTrimmed(ref.atomId)) {
|
|
314
|
+
reasons.push(`${prefix}.ledgerRef.atomId empty`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function isScoreInUnitInterval(score) {
|
|
318
|
+
return typeof score === "number" && Number.isFinite(score) && score >= 0 && score <= 1;
|
|
319
|
+
}
|
|
320
|
+
export function validateEvidenceAtom(atom) {
|
|
321
|
+
const reasons = [];
|
|
322
|
+
if (!isRecord(atom)) {
|
|
323
|
+
return buildResult(["atom invalid"]);
|
|
324
|
+
}
|
|
325
|
+
pushIf(reasons, schemaMismatch(atom.schemaVersion, CONNECTED_CONTEXT_SCHEMA_VERSION), "atom.schemaVersion mismatch");
|
|
326
|
+
pushIf(reasons, !isNonEmptyTrimmed(atom.stableId), "atom.stableId empty");
|
|
327
|
+
pushIf(reasons, !isValidScopePath(atom.scopePath, { mustBeRelative: true }), "atom.scopePath invalid");
|
|
328
|
+
if (atom.lineRange !== undefined) {
|
|
329
|
+
pushIf(reasons, !isValidLineRange(atom.lineRange), "atom.lineRange invalid");
|
|
330
|
+
}
|
|
331
|
+
pushIf(reasons, !isScoreInUnitInterval(atom.score), "atom.score out of range");
|
|
332
|
+
const provenance = atom.provenance;
|
|
333
|
+
if (typeof provenance !== "object" || provenance === null) {
|
|
334
|
+
reasons.push("atom.provenance missing");
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
pushIf(reasons, !EVIDENCE_ATOM_PROVENANCE_KINDS.includes(atom.provenance.kind), "atom.provenance.kind invalid");
|
|
338
|
+
pushIf(reasons, !isNonEmptyTrimmed(atom.provenance.tool), "atom.provenance.tool empty");
|
|
339
|
+
pushIf(reasons, !isNonEmptyTrimmed(atom.provenance.queryFingerprint), "atom.provenance.queryFingerprint empty");
|
|
340
|
+
}
|
|
341
|
+
pushIf(reasons, !EVIDENCE_ATOM_REDACTION_STATES.includes(atom.redactionState), "atom.redactionState invalid");
|
|
342
|
+
pushIf(reasons, !isFiniteNonNegativeInteger(atom.emittedAtMs), "atom.emittedAtMs invalid");
|
|
343
|
+
if (atom.ledgerRef !== undefined) {
|
|
344
|
+
validateLedgerRef(atom.ledgerRef, reasons, "atom");
|
|
345
|
+
}
|
|
346
|
+
return buildResult(reasons);
|
|
347
|
+
}
|
|
348
|
+
export function validateRetrievalQuery(query) {
|
|
349
|
+
const reasons = [];
|
|
350
|
+
if (!isRecord(query)) {
|
|
351
|
+
return buildResult(["query invalid"]);
|
|
352
|
+
}
|
|
353
|
+
pushIf(reasons, !RETRIEVAL_QUERY_KINDS.includes(query.kind), "query.kind invalid");
|
|
354
|
+
pushIf(reasons, !isNonEmptyTrimmed(query.text), "query.text empty");
|
|
355
|
+
pushIf(reasons, !isFinitePositiveInteger(query.maxResults), "query.maxResults invalid");
|
|
356
|
+
pushIf(reasons, !isFiniteNonNegativeInteger(query.emittedAtMs), "query.emittedAtMs invalid");
|
|
357
|
+
return buildResult(reasons);
|
|
358
|
+
}
|
|
359
|
+
// UTF-8 byte length via WHATWG TextEncoder (Web standard; available on globalThis in Node 22).
|
|
360
|
+
// Using this instead of Buffer keeps the contracts package free of Node-only APIs.
|
|
361
|
+
const TEXT_ENCODER = new TextEncoder();
|
|
362
|
+
function utf8ByteLength(value) {
|
|
363
|
+
return TEXT_ENCODER.encode(value).length;
|
|
364
|
+
}
|
|
365
|
+
function validatePackFileEntry(entry, scope, selectedPaths, reasons) {
|
|
366
|
+
if (!isRecord(entry)) {
|
|
367
|
+
reasons.push("pack.files entry invalid");
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const scopePath = entry.scopePath;
|
|
371
|
+
if (typeof scopePath === "string" && isValidScopePath(scopePath, { mustBeRelative: true })) {
|
|
372
|
+
if (selectedPaths.has(scopePath)) {
|
|
373
|
+
reasons.push("pack.files contains duplicate scopePath");
|
|
374
|
+
}
|
|
375
|
+
else if (setHasOverlappingPath(selectedPaths, scopePath)) {
|
|
376
|
+
reasons.push("pack.files contains overlapping scopePath");
|
|
377
|
+
}
|
|
378
|
+
selectedPaths.add(scopePath);
|
|
379
|
+
}
|
|
380
|
+
if (!isConnectedFileRole(entry.role)) {
|
|
381
|
+
reasons.push("pack.files entry has invalid role");
|
|
382
|
+
}
|
|
383
|
+
if (!isValidScopePath(scopePath, { mustBeRelative: true })) {
|
|
384
|
+
reasons.push("pack.files entry has invalid scopePath");
|
|
385
|
+
}
|
|
386
|
+
if (!isPathWithinSelectedScope(scope, scopePath)) {
|
|
387
|
+
reasons.push("pack.files entry falls outside selected scope");
|
|
388
|
+
}
|
|
389
|
+
if (!isNonEmptyTrimmed(entry.selectionReason)) {
|
|
390
|
+
reasons.push("pack.files entry has empty selectionReason");
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
function validatePackExcerptAtom(atom, entryScopePath, scope, reasons) {
|
|
394
|
+
const atomScopePath = isRecord(atom) ? atom.scopePath : undefined;
|
|
395
|
+
if (atomScopePath !== entryScopePath) {
|
|
396
|
+
reasons.push("pack.files excerpt atom.scopePath does not match parent scopePath");
|
|
397
|
+
}
|
|
398
|
+
appendPrefixedReasons(validateEvidenceAtom(atom), "pack.files excerpt ", reasons);
|
|
399
|
+
if (typeof atomScopePath === "string" && !isPathWithinSelectedScope(scope, atomScopePath)) {
|
|
400
|
+
reasons.push("pack.files excerpt atom.scopePath falls outside selected scope");
|
|
401
|
+
}
|
|
402
|
+
if (isRecord(atom) && atom.redactionState === "raw-internal") {
|
|
403
|
+
reasons.push("pack.files excerpt atom.redactionState raw-internal");
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
function validatePackExcerptContent(excerpt, reasons) {
|
|
407
|
+
if (typeof excerpt.content !== "string") {
|
|
408
|
+
reasons.push("pack.files excerpt content invalid");
|
|
409
|
+
}
|
|
410
|
+
if (!isFiniteNonNegativeInteger(excerpt.contentBytes)) {
|
|
411
|
+
reasons.push("pack.files excerpt contentBytes invalid");
|
|
412
|
+
return 0;
|
|
413
|
+
}
|
|
414
|
+
if (typeof excerpt.content === "string" &&
|
|
415
|
+
excerpt.contentBytes !== utf8ByteLength(excerpt.content)) {
|
|
416
|
+
reasons.push("pack.files excerpt contentBytes mismatch");
|
|
417
|
+
}
|
|
418
|
+
return excerpt.contentBytes;
|
|
419
|
+
}
|
|
420
|
+
function validatePackExcerpt(excerpt, entryScopePath, scope, reasons) {
|
|
421
|
+
if (!isRecord(excerpt)) {
|
|
422
|
+
reasons.push("pack.files excerpt invalid");
|
|
423
|
+
return 0;
|
|
424
|
+
}
|
|
425
|
+
validatePackExcerptAtom(excerpt.atom, entryScopePath, scope, reasons);
|
|
426
|
+
return validatePackExcerptContent(excerpt, reasons);
|
|
427
|
+
}
|
|
428
|
+
function collectPackAtomIds(files) {
|
|
429
|
+
const atomIds = new Set();
|
|
430
|
+
if (!Array.isArray(files)) {
|
|
431
|
+
return atomIds;
|
|
432
|
+
}
|
|
433
|
+
for (const entry of files) {
|
|
434
|
+
if (!isRecord(entry) || !Array.isArray(entry.excerpts)) {
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
for (const excerpt of entry.excerpts) {
|
|
438
|
+
if (!isRecord(excerpt) || !isRecord(excerpt.atom)) {
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
const stableId = excerpt.atom.stableId;
|
|
442
|
+
if (typeof stableId === "string") {
|
|
443
|
+
atomIds.add(stableId);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return atomIds;
|
|
448
|
+
}
|
|
449
|
+
function validatePackFiles(files, scope, reasons) {
|
|
450
|
+
const selectedPaths = new Set();
|
|
451
|
+
let actualExcerptBytes = 0;
|
|
452
|
+
if (!Array.isArray(files)) {
|
|
453
|
+
reasons.push("pack.files invalid");
|
|
454
|
+
return { actualExcerptBytes, selectedPaths };
|
|
455
|
+
}
|
|
456
|
+
for (const entry of files) {
|
|
457
|
+
validatePackFileEntry(entry, scope, selectedPaths, reasons);
|
|
458
|
+
if (!isRecord(entry)) {
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
if (!Array.isArray(entry.excerpts)) {
|
|
462
|
+
reasons.push("pack.files entry excerpts invalid");
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
const parentScopePath = typeof entry.scopePath === "string" ? entry.scopePath : "";
|
|
466
|
+
for (const excerpt of entry.excerpts) {
|
|
467
|
+
actualExcerptBytes += validatePackExcerpt(excerpt, parentScopePath, scope, reasons);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return { actualExcerptBytes, selectedPaths };
|
|
471
|
+
}
|
|
472
|
+
function validateOmittedPathState(entry, entryIndex, reasons, selectedPaths, omittedPaths) {
|
|
473
|
+
const scopePath = entry.scopePath;
|
|
474
|
+
const validScopePath = isValidScopePath(scopePath, { mustBeRelative: true });
|
|
475
|
+
if (typeof scopePath === "string" && setHasOverlappingPath(selectedPaths, scopePath)) {
|
|
476
|
+
reasons.push("pack.omitted overlaps selected scopePath");
|
|
477
|
+
}
|
|
478
|
+
if (typeof scopePath === "string" && validScopePath) {
|
|
479
|
+
if (omittedPaths.has(scopePath)) {
|
|
480
|
+
reasons.push("pack.omitted contains duplicate scopePath");
|
|
481
|
+
}
|
|
482
|
+
else if (setHasOverlappingPath(omittedPaths, scopePath)) {
|
|
483
|
+
reasons.push("pack.omitted contains overlapping scopePath");
|
|
484
|
+
}
|
|
485
|
+
omittedPaths.add(scopePath);
|
|
486
|
+
}
|
|
487
|
+
if (!validScopePath) {
|
|
488
|
+
reasons.push(`omitted[${entryIndex.toString()}].scopePath invalid`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
function validatePackOmitted(entries, scope, reasons, selectedPaths) {
|
|
492
|
+
if (!Array.isArray(entries)) {
|
|
493
|
+
reasons.push("pack.omitted invalid");
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const omittedPaths = new Set();
|
|
497
|
+
for (const [i, entry] of entries.entries()) {
|
|
498
|
+
if (!isRecord(entry)) {
|
|
499
|
+
reasons.push("pack.omitted entry invalid");
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
validateOmittedPathState(entry, i, reasons, selectedPaths, omittedPaths);
|
|
503
|
+
if (!isCandidateOmissionReason(entry.reason)) {
|
|
504
|
+
reasons.push("pack.omitted has invalid reason");
|
|
505
|
+
}
|
|
506
|
+
if (!isPathWithinSelectedScope(scope, entry.scopePath)) {
|
|
507
|
+
reasons.push("pack.omitted entry falls outside selected scope");
|
|
508
|
+
}
|
|
509
|
+
if (!isFiniteNonNegativeInteger(entry.omittedAtMs)) {
|
|
510
|
+
reasons.push("pack.omitted has invalid omittedAtMs");
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
function validateImpactedAtomIds(impactedAtomIds, entryIndex, validAtomIds, reasons) {
|
|
515
|
+
if (!Array.isArray(impactedAtomIds)) {
|
|
516
|
+
reasons.push(`uncertainty[${entryIndex.toString()}].impactedAtomIds invalid`);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const impactedIds = new Set();
|
|
520
|
+
for (const atomId of impactedAtomIds) {
|
|
521
|
+
if (!isNonEmptyTrimmed(atomId)) {
|
|
522
|
+
reasons.push(`uncertainty[${entryIndex.toString()}].impactedAtomIds invalid`);
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
if (impactedIds.has(atomId)) {
|
|
526
|
+
reasons.push(`uncertainty[${entryIndex.toString()}].impactedAtomIds duplicate`);
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
if (!validAtomIds.has(atomId)) {
|
|
530
|
+
reasons.push(`uncertainty[${entryIndex.toString()}].impactedAtomIds unknown`);
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
impactedIds.add(atomId);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
function validateUncertaintyEntry(entry, entryIndex, validAtomIds, reasons) {
|
|
537
|
+
if (!isUncertaintyMarkerKind(entry.kind)) {
|
|
538
|
+
reasons.push("pack.uncertainty has invalid kind");
|
|
539
|
+
}
|
|
540
|
+
if (!isNonEmptyTrimmed(entry.claim)) {
|
|
541
|
+
reasons.push(`uncertainty[${entryIndex.toString()}].claim empty`);
|
|
542
|
+
}
|
|
543
|
+
if (!isFiniteNonNegativeInteger(entry.emittedAtMs)) {
|
|
544
|
+
reasons.push("pack.uncertainty has invalid emittedAtMs");
|
|
545
|
+
}
|
|
546
|
+
validateImpactedAtomIds(entry.impactedAtomIds, entryIndex, validAtomIds, reasons);
|
|
547
|
+
}
|
|
548
|
+
function validatePackUncertainty(entries, validAtomIds, reasons) {
|
|
549
|
+
if (!Array.isArray(entries)) {
|
|
550
|
+
reasons.push("pack.uncertainty invalid");
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
for (const [i, entry] of entries.entries()) {
|
|
554
|
+
if (!isRecord(entry)) {
|
|
555
|
+
reasons.push("pack.uncertainty entry invalid");
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
validateUncertaintyEntry(entry, i, validAtomIds, reasons);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function checkBudgetDimension(used, cap, dimension, reasons) {
|
|
562
|
+
if (!isFiniteNonNegativeInteger(cap)) {
|
|
563
|
+
reasons.push(`budget.${dimension}Max not a finite non-negative integer`);
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
if (!Number.isFinite(used) || used < 0) {
|
|
567
|
+
reasons.push(`pack.usage.${dimension} invalid`);
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
if (used > cap) {
|
|
571
|
+
reasons.push(`pack.usage.${dimension} exceeds budget`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
function validatePackBudget(pack, actualExcerptBytes, reasons) {
|
|
575
|
+
if (!isRecord(pack.usage)) {
|
|
576
|
+
reasons.push("pack.usage invalid");
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
if (!isRecord(pack.budget)) {
|
|
580
|
+
reasons.push("pack.budget invalid");
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
checkBudgetDimension(pack.usage.searchCalls, pack.budget.searchCallsMax, "searchCalls", reasons);
|
|
584
|
+
checkBudgetDimension(pack.usage.filesRead, pack.budget.filesReadMax, "filesRead", reasons);
|
|
585
|
+
checkBudgetDimension(pack.usage.excerptBytes, pack.budget.excerptBytesMax, "excerptBytes", reasons);
|
|
586
|
+
checkBudgetDimension(pack.usage.modelInputTokens, pack.budget.modelInputTokensMax, "modelInputTokens", reasons);
|
|
587
|
+
checkBudgetDimension(pack.usage.modelOutputTokens, pack.budget.modelOutputTokensMax, "modelOutputTokens", reasons);
|
|
588
|
+
checkBudgetDimension(pack.usage.elapsedMs, pack.budget.elapsedMsMax, "elapsedMs", reasons);
|
|
589
|
+
checkBudgetDimension(pack.usage.rerankCalls, pack.budget.rerankCallsMax, "rerankCalls", reasons);
|
|
590
|
+
if (actualExcerptBytes > pack.usage.excerptBytes) {
|
|
591
|
+
reasons.push("pack.files excerpts exceed pack.usage.excerptBytes");
|
|
592
|
+
}
|
|
593
|
+
if (actualExcerptBytes > pack.budget.excerptBytesMax) {
|
|
594
|
+
reasons.push("pack.files excerpts exceed budget.excerptBytesMax");
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function validatePackScopeAndQuery(pack, reasons) {
|
|
598
|
+
appendPrefixedReasons(validateSelectedScope(pack.scope), "pack.", reasons);
|
|
599
|
+
appendPrefixedReasons(validateRetrievalQuery(pack.query), "pack.", reasons);
|
|
600
|
+
}
|
|
601
|
+
function validatePackFileCollection(pack, reasons) {
|
|
602
|
+
if (isRuntimeSelectedScope(pack.scope)) {
|
|
603
|
+
return validatePackFiles(pack.files, pack.scope, reasons);
|
|
604
|
+
}
|
|
605
|
+
if (!Array.isArray(pack.files)) {
|
|
606
|
+
reasons.push("pack.files invalid");
|
|
607
|
+
}
|
|
608
|
+
return { actualExcerptBytes: 0, selectedPaths: new Set() };
|
|
609
|
+
}
|
|
610
|
+
function validatePackOmittedCollection(pack, fileSummary, reasons) {
|
|
611
|
+
if (isRuntimeSelectedScope(pack.scope)) {
|
|
612
|
+
validatePackOmitted(pack.omitted, pack.scope, reasons, fileSummary.selectedPaths);
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
if (!Array.isArray(pack.omitted)) {
|
|
616
|
+
reasons.push("pack.omitted invalid");
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
export function validateConnectedContextPack(pack) {
|
|
620
|
+
const reasons = [];
|
|
621
|
+
if (!isRecord(pack)) {
|
|
622
|
+
return buildResult(["pack invalid"]);
|
|
623
|
+
}
|
|
624
|
+
pushIf(reasons, schemaMismatch(pack.schemaVersion, CONNECTED_CONTEXT_SCHEMA_VERSION), "pack.schemaVersion mismatch");
|
|
625
|
+
pushIf(reasons, !isNonEmptyTrimmed(pack.stableId), "pack.stableId empty");
|
|
626
|
+
validatePackScopeAndQuery(pack, reasons);
|
|
627
|
+
const fileSummary = validatePackFileCollection(pack, reasons);
|
|
628
|
+
validatePackBudget(pack, fileSummary.actualExcerptBytes, reasons);
|
|
629
|
+
validatePackOmittedCollection(pack, fileSummary, reasons);
|
|
630
|
+
validatePackUncertainty(pack.uncertainty, collectPackAtomIds(pack.files), reasons);
|
|
631
|
+
pushIf(reasons, !isFiniteNonNegativeInteger(pack.emittedAtMs), "pack.emittedAtMs invalid");
|
|
632
|
+
if (pack.ledgerRef !== undefined) {
|
|
633
|
+
validateLedgerRef(pack.ledgerRef, reasons, "pack");
|
|
634
|
+
}
|
|
635
|
+
return buildResult(reasons);
|
|
636
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface ConversationBudgetMessage {
|
|
2
|
+
readonly role: string;
|
|
3
|
+
readonly content: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ConversationBudgetDocumentContext {
|
|
6
|
+
readonly extractedBytes: number;
|
|
7
|
+
}
|
|
8
|
+
export interface ConversationBudgetInputs {
|
|
9
|
+
readonly modelContextWindow: number;
|
|
10
|
+
readonly modelMaxOutputTokens: number;
|
|
11
|
+
readonly userDraftText: string;
|
|
12
|
+
readonly conversationHistory: readonly ConversationBudgetMessage[];
|
|
13
|
+
readonly documentContext?: readonly ConversationBudgetDocumentContext[] | undefined;
|
|
14
|
+
readonly repoContextPackBytes?: number | undefined;
|
|
15
|
+
readonly knowledgeCapsuleBytes?: number | undefined;
|
|
16
|
+
readonly memoryContextBytes?: number | undefined;
|
|
17
|
+
}
|
|
18
|
+
export type ConversationBudgetPressure = "low" | "moderate" | "high" | "exceeded";
|
|
19
|
+
export interface ConversationBudgetBreakdown {
|
|
20
|
+
readonly draftBytes: number;
|
|
21
|
+
readonly historyBytes: number;
|
|
22
|
+
readonly documentBytes: number;
|
|
23
|
+
readonly repoContextBytes: number;
|
|
24
|
+
readonly knowledgeBytes: number;
|
|
25
|
+
readonly memoryBytes: number;
|
|
26
|
+
}
|
|
27
|
+
export interface ConversationBudgetEstimate {
|
|
28
|
+
readonly approximateBytes: number;
|
|
29
|
+
readonly approximateTokens: number;
|
|
30
|
+
readonly contextWindowTokens: number;
|
|
31
|
+
readonly reservedOutputTokens: number;
|
|
32
|
+
readonly availableInputTokens: number;
|
|
33
|
+
readonly pressure: ConversationBudgetPressure;
|
|
34
|
+
readonly breakdown: ConversationBudgetBreakdown;
|
|
35
|
+
}
|
|
36
|
+
export declare function estimateConversationBudget(input: ConversationBudgetInputs): ConversationBudgetEstimate;
|
|
37
|
+
//# sourceMappingURL=conversation-budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-budget.d.ts","sourceRoot":"","sources":["../src/conversation-budget.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,mBAAmB,EAAE,SAAS,yBAAyB,EAAE,CAAC;IACnE,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,iCAAiC,EAAE,GAAG,SAAS,CAAC;IACpF,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnD,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClD;AAED,MAAM,MAAM,0BAA0B,GAAG,KAAK,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC;AAElF,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,0BAA0B,CAAC;IAC9C,QAAQ,CAAC,SAAS,EAAE,2BAA2B,CAAC;CACjD;AA6DD,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,wBAAwB,GAC9B,0BAA0B,CAmC5B"}
|