@oscharko-dev/keiko-contracts 0.2.6 → 0.2.8

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.
@@ -0,0 +1,171 @@
1
+ // Pure structural validators for the context-engineering contracts (ADR-0052). No filesystem,
2
+ // no clock, no crypto, no randomness — every helper inspects the structure of an `unknown`
3
+ // payload and reports which invariants failed. Follows the connected-context.ts isRecord +
4
+ // predicate envelope. STRUCTURAL ONLY: shape, finite non-negative numbers, schemaVersion
5
+ // literal, enum membership, the effectiveInputBudget identity. Policy (e.g. "system-contract
6
+ // must be non-evictable") is NOT enforced here — that belongs to the allocator.
7
+ //
8
+ // Split from context-engineering.ts to keep both files under the 400-LOC budget (mirrors the
9
+ // memory-validation.ts / memory-operations-validation.ts split).
10
+ import { CONTEXT_ENGINEERING_SCHEMA_VERSION, CONTEXT_EVICTION_POLICIES, CONTEXT_LANE_IDS, } from "./context-engineering.js";
11
+ const BUDGET_PRESSURES = ["low", "moderate", "high", "exceeded"];
12
+ // ─── Shared primitives (local; contracts is a leaf, no shared-util import) ──────
13
+ function isRecord(value) {
14
+ return typeof value === "object" && value !== null;
15
+ }
16
+ function isFiniteNonNegativeNumber(value) {
17
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
18
+ }
19
+ function isNonEmptyTrimmed(value) {
20
+ return typeof value === "string" && value.trim().length > 0;
21
+ }
22
+ function pushIf(reasons, condition, reason) {
23
+ if (condition) {
24
+ reasons.push(reason);
25
+ }
26
+ }
27
+ function buildResult(reasons) {
28
+ return reasons.length === 0 ? { ok: true } : { ok: false, reasons };
29
+ }
30
+ function schemaMismatch(actual) {
31
+ return actual !== CONTEXT_ENGINEERING_SCHEMA_VERSION;
32
+ }
33
+ // ─── Enum guards ────────────────────────────────────────────────────────────────
34
+ export function isContextLaneId(value) {
35
+ return typeof value === "string" && CONTEXT_LANE_IDS.includes(value);
36
+ }
37
+ function isEvictionPolicy(value) {
38
+ return (typeof value === "string" && CONTEXT_EVICTION_POLICIES.includes(value));
39
+ }
40
+ function isBudgetPressure(value) {
41
+ return typeof value === "string" && BUDGET_PRESSURES.includes(value);
42
+ }
43
+ // ─── ContextProfile ─────────────────────────────────────────────────────────────
44
+ function collectProfileReasons(profile, prefix) {
45
+ const reasons = [];
46
+ pushIf(reasons, schemaMismatch(profile.schemaVersion), `${prefix}.schemaVersion mismatch`);
47
+ pushIf(reasons, !isFiniteNonNegativeNumber(profile.maxInputTokens), `${prefix}.maxInputTokens invalid`);
48
+ pushIf(reasons, !isFiniteNonNegativeNumber(profile.reservedOutputTokens), `${prefix}.reservedOutputTokens invalid`);
49
+ pushIf(reasons, !isFiniteNonNegativeNumber(profile.safetyMarginTokens), `${prefix}.safetyMarginTokens invalid`);
50
+ pushIf(reasons, !isFiniteNonNegativeNumber(profile.effectiveInputBudget), `${prefix}.effectiveInputBudget invalid`);
51
+ pushIf(reasons, !isNonEmptyTrimmed(profile.tokenEstimatorId), `${prefix}.tokenEstimatorId empty`);
52
+ return reasons;
53
+ }
54
+ function checkBudgetIdentity(profile, prefix) {
55
+ const max = profile.maxInputTokens;
56
+ const reserved = profile.reservedOutputTokens;
57
+ const safety = profile.safetyMarginTokens;
58
+ const effective = profile.effectiveInputBudget;
59
+ if (typeof max !== "number" ||
60
+ typeof reserved !== "number" ||
61
+ typeof safety !== "number" ||
62
+ typeof effective !== "number") {
63
+ return [];
64
+ }
65
+ const expected = Math.max(0, max - reserved - safety);
66
+ return effective === expected ? [] : [`${prefix}.effectiveInputBudget mismatch`];
67
+ }
68
+ function validateModelMetadata(value, prefix) {
69
+ if (value === undefined) {
70
+ return [];
71
+ }
72
+ if (!isRecord(value)) {
73
+ return [`${prefix}.model invalid`];
74
+ }
75
+ const reasons = [];
76
+ for (const key of ["id", "provider", "notes"]) {
77
+ const field = value[key];
78
+ pushIf(reasons, field !== undefined && typeof field !== "string", `${prefix}.model.${key} invalid`);
79
+ }
80
+ return reasons;
81
+ }
82
+ function collectProfile(value, prefix) {
83
+ if (!isRecord(value)) {
84
+ return [`${prefix} invalid`];
85
+ }
86
+ const reasons = collectProfileReasons(value, prefix);
87
+ reasons.push(...checkBudgetIdentity(value, prefix));
88
+ reasons.push(...validateModelMetadata(value.model, prefix));
89
+ return reasons;
90
+ }
91
+ export function validateContextProfile(value) {
92
+ return buildResult(collectProfile(value, "profile"));
93
+ }
94
+ // ─── ContextLaneBudget ────────────────────────────────────────────────────────
95
+ function collectLaneBudget(value, prefix) {
96
+ if (!isRecord(value)) {
97
+ return [`${prefix} invalid`];
98
+ }
99
+ const reasons = [];
100
+ pushIf(reasons, !isContextLaneId(value.laneId), `${prefix}.laneId invalid`);
101
+ pushIf(reasons, !isFiniteNonNegativeNumber(value.priority), `${prefix}.priority invalid`);
102
+ pushIf(reasons, !isFiniteNonNegativeNumber(value.maxTokens), `${prefix}.maxTokens invalid`);
103
+ pushIf(reasons, !isFiniteNonNegativeNumber(value.minReservedTokens), `${prefix}.minReservedTokens invalid`);
104
+ pushIf(reasons, !isEvictionPolicy(value.eviction), `${prefix}.eviction invalid`);
105
+ return reasons;
106
+ }
107
+ // ─── ContextBudget ──────────────────────────────────────────────────────────────
108
+ export function validateContextBudget(value) {
109
+ if (!isRecord(value)) {
110
+ return buildResult(["budget invalid"]);
111
+ }
112
+ const reasons = [];
113
+ pushIf(reasons, schemaMismatch(value.schemaVersion), "budget.schemaVersion mismatch");
114
+ reasons.push(...collectProfile(value.profile, "budget.profile"));
115
+ if (!Array.isArray(value.lanes)) {
116
+ reasons.push("budget.lanes invalid");
117
+ return buildResult(reasons);
118
+ }
119
+ value.lanes.forEach((lane, index) => {
120
+ reasons.push(...collectLaneBudget(lane, `budget.lanes[${String(index)}]`));
121
+ });
122
+ return buildResult(reasons);
123
+ }
124
+ // ─── ContextLaneDiagnostics ──────────────────────────────────────────────────────
125
+ function collectLaneDiagnostics(value, prefix) {
126
+ if (!isRecord(value)) {
127
+ return [`${prefix} invalid`];
128
+ }
129
+ const reasons = [];
130
+ pushIf(reasons, !isContextLaneId(value.laneId), `${prefix}.laneId invalid`);
131
+ pushIf(reasons, !isFiniteNonNegativeNumber(value.estimatedTokens), `${prefix}.estimatedTokens invalid`);
132
+ pushIf(reasons, !isFiniteNonNegativeNumber(value.includedItems), `${prefix}.includedItems invalid`);
133
+ pushIf(reasons, !isFiniteNonNegativeNumber(value.excludedItems), `${prefix}.excludedItems invalid`);
134
+ pushIf(reasons, !isBudgetPressure(value.budgetPressure), `${prefix}.budgetPressure invalid`);
135
+ pushIf(reasons, value.compactionReason !== undefined && typeof value.compactionReason !== "string", `${prefix}.compactionReason invalid`);
136
+ reasons.push(...collectProvenanceCounts(value.provenanceCounts, prefix));
137
+ return reasons;
138
+ }
139
+ function collectProvenanceCounts(value, prefix) {
140
+ if (value === undefined) {
141
+ return [];
142
+ }
143
+ if (!isRecord(value)) {
144
+ return [`${prefix}.provenanceCounts invalid`];
145
+ }
146
+ const reasons = [];
147
+ for (const count of Object.values(value)) {
148
+ pushIf(reasons, !isFiniteNonNegativeNumber(count), `${prefix}.provenanceCounts value invalid`);
149
+ }
150
+ return reasons;
151
+ }
152
+ // ─── ContextAssemblyDiagnostics ──────────────────────────────────────────────────
153
+ export function validateContextAssemblyDiagnostics(value) {
154
+ if (!isRecord(value)) {
155
+ return buildResult(["assembly invalid"]);
156
+ }
157
+ const reasons = [];
158
+ pushIf(reasons, schemaMismatch(value.schemaVersion), "assembly.schemaVersion mismatch");
159
+ reasons.push(...collectProfile(value.profile, "assembly.profile"));
160
+ pushIf(reasons, !isFiniteNonNegativeNumber(value.totalEstimatedTokens), "assembly.totalEstimatedTokens invalid");
161
+ pushIf(reasons, !isBudgetPressure(value.budgetPressure), "assembly.budgetPressure invalid");
162
+ pushIf(reasons, typeof value.orderedForRecency !== "boolean", "assembly.orderedForRecency invalid");
163
+ if (!Array.isArray(value.lanes)) {
164
+ reasons.push("assembly.lanes invalid");
165
+ return buildResult(reasons);
166
+ }
167
+ value.lanes.forEach((lane, index) => {
168
+ reasons.push(...collectLaneDiagnostics(lane, `assembly.lanes[${String(index)}]`));
169
+ });
170
+ return buildResult(reasons);
171
+ }
@@ -0,0 +1,148 @@
1
+ export declare const CONTEXT_ENGINEERING_SCHEMA_VERSION: "1";
2
+ export declare const DEFAULT_TOKEN_ESTIMATOR_ID: "keiko-conservative-utf8-v1";
3
+ export type ContextLaneId = "system-contract" | "user-task" | "active-plan" | "repo-evidence" | "tool-observations" | "working-memory" | "history-summary" | "verification-evidence";
4
+ export declare const CONTEXT_LANE_IDS: readonly ContextLaneId[];
5
+ export type ContextEvictionPolicy = "none" | "summarize-then-drop" | "drop-oldest" | "drop-lowest-score";
6
+ export declare const CONTEXT_EVICTION_POLICIES: readonly ContextEvictionPolicy[];
7
+ export type ContextBudgetPressure = "low" | "moderate" | "high" | "exceeded";
8
+ export interface ContextModelMetadata {
9
+ readonly id?: string | undefined;
10
+ readonly provider?: string | undefined;
11
+ readonly notes?: string | undefined;
12
+ }
13
+ export interface ContextProfile {
14
+ readonly schemaVersion: typeof CONTEXT_ENGINEERING_SCHEMA_VERSION;
15
+ readonly maxInputTokens: number;
16
+ readonly reservedOutputTokens: number;
17
+ readonly safetyMarginTokens: number;
18
+ readonly effectiveInputBudget: number;
19
+ readonly tokenEstimatorId: string;
20
+ readonly model?: ContextModelMetadata | undefined;
21
+ }
22
+ export declare const DEFAULT_CONTEXT_PROFILE: ContextProfile;
23
+ export interface ContextLaneBudget {
24
+ readonly laneId: ContextLaneId;
25
+ readonly priority: number;
26
+ readonly maxTokens: number;
27
+ readonly minReservedTokens: number;
28
+ readonly eviction: ContextEvictionPolicy;
29
+ }
30
+ export interface ContextBudget {
31
+ readonly schemaVersion: typeof CONTEXT_ENGINEERING_SCHEMA_VERSION;
32
+ readonly profile: ContextProfile;
33
+ readonly lanes: readonly ContextLaneBudget[];
34
+ }
35
+ export interface ContextLane {
36
+ readonly id: ContextLaneId;
37
+ readonly purpose: string;
38
+ readonly priority: number;
39
+ readonly budget: ContextLaneBudget;
40
+ readonly diagnostics?: ContextLaneDiagnostics | undefined;
41
+ }
42
+ export interface ContextLaneDiagnostics {
43
+ readonly laneId: ContextLaneId;
44
+ readonly estimatedTokens: number;
45
+ readonly includedItems: number;
46
+ readonly excludedItems: number;
47
+ readonly budgetPressure: ContextBudgetPressure;
48
+ readonly compactionReason?: string | undefined;
49
+ readonly provenanceCounts?: Readonly<Record<string, number>> | undefined;
50
+ }
51
+ export interface ContextAssemblyDiagnostics {
52
+ readonly schemaVersion: typeof CONTEXT_ENGINEERING_SCHEMA_VERSION;
53
+ readonly profile: ContextProfile;
54
+ readonly totalEstimatedTokens: number;
55
+ readonly budgetPressure: ContextBudgetPressure;
56
+ readonly lanes: readonly ContextLaneDiagnostics[];
57
+ readonly orderedForRecency: boolean;
58
+ }
59
+ export type ContextProvenanceRefKind = "repo-file" | "tool-result" | "evidence-atom" | "message";
60
+ export interface ContextProvenanceRef {
61
+ readonly kind: ContextProvenanceRefKind;
62
+ readonly stableId: string;
63
+ readonly scopePath?: string | undefined;
64
+ readonly lineRange?: {
65
+ readonly startLine: number;
66
+ readonly endLine: number;
67
+ } | undefined;
68
+ readonly contentHash?: string | undefined;
69
+ readonly evidenceAtomId?: string | undefined;
70
+ readonly notPersistedReason?: string | undefined;
71
+ }
72
+ export interface ContextPreservedFact {
73
+ readonly statement: string;
74
+ readonly sourceRef?: ContextProvenanceRef | undefined;
75
+ readonly inferred?: boolean | undefined;
76
+ readonly corroborating?: readonly ContextProvenanceRef[] | undefined;
77
+ readonly rationale?: never;
78
+ readonly confidence?: never;
79
+ }
80
+ export interface ContextAssumption {
81
+ readonly statement: string;
82
+ readonly rationale: string;
83
+ readonly confidence: "low" | "medium" | "high";
84
+ readonly sourceRef?: never;
85
+ readonly inferred?: never;
86
+ }
87
+ export interface ContextUserConstraint {
88
+ readonly statement: string;
89
+ readonly sourceRef?: ContextProvenanceRef | undefined;
90
+ }
91
+ export interface ContextCommandOutcome {
92
+ readonly command: string;
93
+ readonly exitCode: number;
94
+ readonly summary: string;
95
+ }
96
+ export interface ContextInvalidationKey {
97
+ readonly scopePath: string;
98
+ readonly contentHash: string;
99
+ }
100
+ export interface ContextCompactionRecord {
101
+ readonly schemaVersion: typeof CONTEXT_ENGINEERING_SCHEMA_VERSION;
102
+ readonly laneId: ContextLaneId;
103
+ readonly reason: string;
104
+ readonly itemsBefore: number;
105
+ readonly itemsAfter: number;
106
+ readonly tokensBefore: number;
107
+ readonly tokensAfter: number;
108
+ readonly summaryRefHash?: string | undefined;
109
+ readonly rehydration?: ContextRehydrationHandle | undefined;
110
+ readonly orderedAt?: number | undefined;
111
+ readonly sourceSpans?: readonly ContextProvenanceRef[] | undefined;
112
+ readonly preservedFacts?: readonly ContextPreservedFact[] | undefined;
113
+ readonly assumptions?: readonly ContextAssumption[] | undefined;
114
+ readonly userConstraints?: readonly ContextUserConstraint[] | undefined;
115
+ readonly decisions?: readonly string[] | undefined;
116
+ readonly openQuestions?: readonly string[] | undefined;
117
+ readonly filesInspected?: readonly string[] | undefined;
118
+ readonly filesChanged?: readonly string[] | undefined;
119
+ readonly commandOutcomes?: readonly ContextCommandOutcome[] | undefined;
120
+ readonly failingTests?: readonly string[] | undefined;
121
+ readonly droppedCategories?: readonly string[] | undefined;
122
+ readonly invalidationKeys?: readonly ContextInvalidationKey[] | undefined;
123
+ }
124
+ export interface ContextRehydrationHandle {
125
+ readonly schemaVersion: typeof CONTEXT_ENGINEERING_SCHEMA_VERSION;
126
+ readonly laneId: ContextLaneId;
127
+ readonly handleId: string;
128
+ readonly itemCount: number;
129
+ readonly approxTokens: number;
130
+ readonly kind?: ContextProvenanceRefKind | undefined;
131
+ readonly scopePath?: string | undefined;
132
+ readonly lineRange?: {
133
+ readonly startLine: number;
134
+ readonly endLine: number;
135
+ } | undefined;
136
+ readonly contentHash?: string | undefined;
137
+ readonly evidenceAtomId?: string | undefined;
138
+ readonly notPersistedReason?: string | undefined;
139
+ readonly approvedSummary?: string | undefined;
140
+ }
141
+ export declare function estimateTokens(text: string): number;
142
+ export declare function estimateTokensForSegments(segments: readonly string[]): number;
143
+ export declare function deriveContextProfile(input: {
144
+ readonly maxInputTokens: number;
145
+ readonly reservedOutputTokens: number;
146
+ readonly safetyMarginTokens: number;
147
+ }): ContextProfile;
148
+ //# sourceMappingURL=context-engineering.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-engineering.d.ts","sourceRoot":"","sources":["../src/context-engineering.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ // Pure readonly contracts for the deterministic context-engineering layer (ADR-0052). No IO,
2
+ // no clock, no randomness, no sibling @oscharko-dev/keiko-* import (contracts is a strict leaf).
3
+ // estimateTokens is the single canonical token currency; the allocator that consumes
4
+ // ContextProfile lives in keiko-workflows. Validators follow the connected-context.ts
5
+ // isRecord + predicate envelope and live in the sibling context-engineering-validation.ts to
6
+ // keep both files under the 400-LOC budget (mirrors the memory-validation.ts split).
7
+ export const CONTEXT_ENGINEERING_SCHEMA_VERSION = "1";
8
+ // Provenance string recording which estimator produced a profile's counts. The estimator
9
+ // function itself cannot be a JSON-serializable contract field, so the id is carried instead.
10
+ export const DEFAULT_TOKEN_ESTIMATOR_ID = "keiko-conservative-utf8-v1";
11
+ export const CONTEXT_LANE_IDS = [
12
+ "system-contract",
13
+ "user-task",
14
+ "active-plan",
15
+ "repo-evidence",
16
+ "tool-observations",
17
+ "working-memory",
18
+ "history-summary",
19
+ "verification-evidence",
20
+ ];
21
+ export const CONTEXT_EVICTION_POLICIES = [
22
+ "none",
23
+ "summarize-then-drop",
24
+ "drop-oldest",
25
+ "drop-lowest-score",
26
+ ];
27
+ // Conservative defaults. 128k window, 8k reserved for output, 4k safety margin =>
28
+ // 116k effective input budget. Frozen. `model` is intentionally omitted (optional).
29
+ export const DEFAULT_CONTEXT_PROFILE = {
30
+ schemaVersion: CONTEXT_ENGINEERING_SCHEMA_VERSION,
31
+ maxInputTokens: 128_000,
32
+ reservedOutputTokens: 8_000,
33
+ safetyMarginTokens: 4_000,
34
+ effectiveInputBudget: 116_000,
35
+ tokenEstimatorId: DEFAULT_TOKEN_ESTIMATOR_ID,
36
+ };
37
+ // ─── Canonical token estimator (the single token currency) ─────────────────── [PR1]
38
+ // Deterministic, conservative (slightly over-estimates), total (never throws), offline.
39
+ //
40
+ // bytes-per-token divisor. Deliberately SMALLER than the repo's looser bytes/4 norm so the
41
+ // estimate over-counts versus a typical provider tokenization. Over-estimation is the safe
42
+ // direction: the allocator fits fewer tokens than the provider would, never more.
43
+ const TOKEN_BYTES_PER_TOKEN_DIVISOR = 3.5;
44
+ // Small fixed structural overhead per segment, modelling per-message framing. Empty input
45
+ // returns exactly this (never 0 / NaN).
46
+ const TOKEN_STRUCTURAL_OVERHEAD = 2;
47
+ // Mirrors conversation-budget.ts utf8ByteLength: TextEncoder when available, char-length
48
+ // fallback for any legacy harness without it. Never throws.
49
+ function utf8ByteLength(text) {
50
+ if (typeof TextEncoder !== "undefined") {
51
+ return new TextEncoder().encode(text).length;
52
+ }
53
+ return text.length;
54
+ }
55
+ export function estimateTokens(text) {
56
+ const bytes = utf8ByteLength(text);
57
+ return TOKEN_STRUCTURAL_OVERHEAD + Math.ceil(bytes / TOKEN_BYTES_PER_TOKEN_DIVISOR);
58
+ }
59
+ // Sum estimateTokens over a set of segments (e.g. messages). The per-segment overhead models
60
+ // per-message framing. Total / never throws on [] or empty segments.
61
+ export function estimateTokensForSegments(segments) {
62
+ let total = 0;
63
+ for (const segment of segments) {
64
+ total += estimateTokens(segment);
65
+ }
66
+ return total;
67
+ }
68
+ // Derives effectiveInputBudget = maxInputTokens − reservedOutputTokens − safetyMarginTokens,
69
+ // clamped to >= 0. Pure. Used to build a ContextProfile from a customer model window override.
70
+ export function deriveContextProfile(input) {
71
+ const effective = Math.max(0, input.maxInputTokens - input.reservedOutputTokens - input.safetyMarginTokens);
72
+ return {
73
+ schemaVersion: CONTEXT_ENGINEERING_SCHEMA_VERSION,
74
+ maxInputTokens: input.maxInputTokens,
75
+ reservedOutputTokens: input.reservedOutputTokens,
76
+ safetyMarginTokens: input.safetyMarginTokens,
77
+ effectiveInputBudget: effective,
78
+ tokenEstimatorId: DEFAULT_TOKEN_ESTIMATOR_ID,
79
+ };
80
+ }
@@ -0,0 +1,9 @@
1
+ import type { ContextValidationResult } from "./context-engineering-validation.js";
2
+ export declare function isContextToolObservationKind(value: unknown): boolean;
3
+ export declare function validateContextToolRehydrationHandle(value: unknown): ContextValidationResult;
4
+ export declare function validateShapedCommandObservation(value: unknown): ContextValidationResult;
5
+ export declare function validateShapedTestObservation(value: unknown): ContextValidationResult;
6
+ export declare function validateShapedSearchObservation(value: unknown): ContextValidationResult;
7
+ export declare function validateShapedBrowserObservation(value: unknown): ContextValidationResult;
8
+ export declare function validateContextToolObservation(value: unknown): ContextValidationResult;
9
+ //# sourceMappingURL=context-observations-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-observations-validation.d.ts","sourceRoot":"","sources":["../src/context-observations-validation.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qCAAqC,CAAC;AA6CnF,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEpE;AA0BD,wBAAgB,oCAAoC,CAAC,KAAK,EAAE,OAAO,GAAG,uBAAuB,CAE5F;AAqED,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,OAAO,GAAG,uBAAuB,CAQxF;AA4DD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,uBAAuB,CAQrF;AA6CD,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,uBAAuB,CAQvF;AAwBD,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,OAAO,GAAG,uBAAuB,CAQxF;AAGD,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,uBAAuB,CAgBtF"}