@dsh-enhanced/assistant-growth-contract 0.1.12

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,192 @@
1
+ /** Private Host protocol between Preference Learning and Personal Memory. */
2
+ export declare const PREFERENCE_MEMORY_PROMOTION_PROTOCOL: "assistant-growth/preference-memory-promotion/v1";
3
+ export declare const PREFERENCE_MEMORY_PROMOTION_REQUEST_PROTOCOL: "assistant-growth/preference-memory-promotion-request/v1";
4
+ export declare const PREFERENCE_MEMORY_PROMOTION_SUBMISSION_PROTOCOL: "assistant-growth/preference-memory-promotion-submission/v1";
5
+ export declare const PREFERENCE_MEMORY_PROMOTION_RESULT_PROTOCOL: "assistant-growth/preference-memory-promotion-result/v1";
6
+ export declare const PREFERENCE_MEMORY_PROMOTION_CANCELLATION_PROTOCOL: "assistant-growth/preference-memory-promotion-cancellation/v1";
7
+ /** The first release deliberately supports exactly one low-sensitivity T2 pair. */
8
+ export declare const PREFERENCE_MEMORY_PROMOTION_KEY: "memory.retention";
9
+ export declare const PREFERENCE_MEMORY_PROMOTION_VALUE: "long-term";
10
+ export declare const PREFERENCE_MEMORY_PROMOTION_RENDERER_ID: "memory.retention.long-term/v1";
11
+ export declare const PREFERENCE_MEMORY_PROMOTION_CONTENT: "Retain information the owner explicitly confirms for long-term memory.";
12
+ export interface PreferenceMemoryPromotionScope {
13
+ workspace: string;
14
+ preset: string;
15
+ }
16
+ export interface PreferenceMemoryPromotionPrincipalLineage {
17
+ principalRecordId: string;
18
+ principalVersion: number;
19
+ }
20
+ /** Content-free evidence summary; signal bodies and free-form text never cross this port. */
21
+ export interface PreferenceMemoryPromotionHypothesis {
22
+ id: string;
23
+ key: typeof PREFERENCE_MEMORY_PROMOTION_KEY;
24
+ value: typeof PREFERENCE_MEMORY_PROMOTION_VALUE;
25
+ version: number;
26
+ confidenceBps: number;
27
+ contradictionBps: number;
28
+ supportingSignals: number;
29
+ distinctSignalSources: number;
30
+ evidenceMass: number;
31
+ }
32
+ export interface PreferenceMemoryPromotionRequest {
33
+ contractVersion: 1;
34
+ promotionId: string;
35
+ /** Durable generation of this exact logical promotion, independent of owner generation. */
36
+ promotionGeneration: number;
37
+ requestDigest: string;
38
+ idempotencyKey: string;
39
+ scope: Readonly<PreferenceMemoryPromotionScope>;
40
+ principalId: string;
41
+ principalLineage: Readonly<PreferenceMemoryPromotionPrincipalLineage>;
42
+ ownerGeneration: number;
43
+ hypothesis: Readonly<PreferenceMemoryPromotionHypothesis>;
44
+ rendererId: typeof PREFERENCE_MEMORY_PROMOTION_RENDERER_ID;
45
+ observedAt: number;
46
+ deadlineAt: number;
47
+ }
48
+ /** ACK that Personal Memory durably accepted the immutable request. */
49
+ export interface PreferenceMemoryPromotionSubmissionReceipt {
50
+ contractVersion: 1;
51
+ promotionId: string;
52
+ promotionGeneration: number;
53
+ requestDigest: string;
54
+ outcome: 'accepted' | 'replayed';
55
+ memoryProposalId: string;
56
+ receiptDigest: string;
57
+ }
58
+ export type PreferenceMemoryPromotionTerminalStatus = 'confirmed' | 'rejected' | 'expired' | 'conflicted' | 'stale-owner';
59
+ interface PreferenceMemoryPromotionResultIdentity {
60
+ contractVersion: 1;
61
+ promotionId: string;
62
+ promotionGeneration: number;
63
+ requestDigest: string;
64
+ resultVersion: number;
65
+ occurredAt: number;
66
+ receiptDigest: string;
67
+ }
68
+ /**
69
+ * Authoritative terminal projection produced by Personal Memory. `confirmed`
70
+ * is legal only after its Memory record committed. `rejected` is reserved for
71
+ * an explicit owner rejection; all other failures remain distinct.
72
+ */
73
+ export type PreferenceMemoryPromotionResult = PreferenceMemoryPromotionResultIdentity & ({
74
+ status: 'confirmed';
75
+ memoryProposalId: string;
76
+ memoryProposalVersion: number;
77
+ memoryRecordId: string;
78
+ memoryRecordVersion: number;
79
+ memoryRecordDigest: string;
80
+ } | {
81
+ status: 'rejected';
82
+ rejectionKind: 'owner-explicit';
83
+ memoryProposalId: string;
84
+ memoryProposalVersion: number;
85
+ } | {
86
+ status: 'expired' | 'conflicted';
87
+ memoryProposalId: string;
88
+ memoryProposalVersion: number;
89
+ } | {
90
+ status: 'stale-owner';
91
+ });
92
+ /** Preference commits the projection before returning this Memory outbox ACK. */
93
+ export interface PreferenceMemoryPromotionResultAck {
94
+ contractVersion: 1;
95
+ promotionId: string;
96
+ promotionGeneration: number;
97
+ resultVersion: number;
98
+ receiptDigest: string;
99
+ outcome: 'applied' | 'replayed' | 'cancelled';
100
+ }
101
+ export type PreferenceMemoryPromotionCancellationReason = 'forget' | 'owner-rotated' | 'superseded';
102
+ export interface PreferenceMemoryPromotionCancellationRequest {
103
+ contractVersion: 1;
104
+ promotionId: string;
105
+ promotionGeneration: number;
106
+ requestDigest: string;
107
+ principalLineage: Readonly<PreferenceMemoryPromotionPrincipalLineage>;
108
+ ownerGeneration: number;
109
+ reason: PreferenceMemoryPromotionCancellationReason;
110
+ occurredAt: number;
111
+ cancellationDigest: string;
112
+ }
113
+ export interface PreferenceMemoryPromotionCancellationReceipt {
114
+ contractVersion: 1;
115
+ promotionId: string;
116
+ promotionGeneration: number;
117
+ requestDigest: string;
118
+ cancellationDigest: string;
119
+ /** `already-confirmed` is safe only for non-destructive supersession. */
120
+ outcome: 'cancelled' | 'replayed' | 'already-confirmed';
121
+ receiptDigest: string;
122
+ }
123
+ /** Process-local ownership proof for the Memory-minted registration. */
124
+ export interface PreferencePromotionSourceRegistrationOwner {
125
+ ownsPreferencePromotionSourceRegistration(registration: Readonly<PreferenceMemoryPromotionRegistration>): boolean;
126
+ }
127
+ /**
128
+ * Single bidirectional, revocable capability minted by Personal Memory for one
129
+ * exact live Preference producer generation. Neither side receives the other
130
+ * service's general API.
131
+ */
132
+ export interface PreferenceMemoryPromotionRegistration {
133
+ readonly protocol: typeof PREFERENCE_MEMORY_PROMOTION_PROTOCOL;
134
+ readonly producer: 'personal-memory';
135
+ readonly sourceGeneration: string;
136
+ readonly sinkGeneration: string;
137
+ readonly owner: PreferencePromotionSourceRegistrationOwner;
138
+ propose(request: Readonly<PreferenceMemoryPromotionRequest>): Readonly<PreferenceMemoryPromotionSubmissionReceipt>;
139
+ cancelPromotion(request: Readonly<PreferenceMemoryPromotionCancellationRequest>): Readonly<PreferenceMemoryPromotionCancellationReceipt>;
140
+ listTerminalResults(limit: number): readonly Readonly<PreferenceMemoryPromotionResult>[];
141
+ acknowledgeTerminalResult(ack: Readonly<PreferenceMemoryPromotionResultAck>): void;
142
+ }
143
+ /** Structural Host service contract; authenticity is checked by this shared contract's brand. */
144
+ export interface PreferenceMemoryPromotionProducer {
145
+ trustedMemoryPromotionProducerGeneration(): string;
146
+ registerTrustedMemoryPromotionResultSink(registration: Readonly<PreferenceMemoryPromotionRegistration>): () => void;
147
+ }
148
+ /**
149
+ * Marks the exact live Preference producer owned by the producer package. The
150
+ * private probe lets Cordis wrappers resolve back to that exact object without
151
+ * making a structural lookalike authentic. This is a process-local composition
152
+ * guard, not a sandbox boundary against already-trusted Host code.
153
+ */
154
+ export declare function brandPreferenceMemoryPromotionProducer(producer: PreferenceMemoryPromotionProducer): void;
155
+ /** Revokes the exact producer brand when its owning service is disposed. */
156
+ export declare function unbrandPreferenceMemoryPromotionProducer(producer: PreferenceMemoryPromotionProducer): void;
157
+ /**
158
+ * Resolves a Cordis forwarding wrapper to the exact producer object that owns
159
+ * the process-local brand. Consumers must bind and invoke this object, never
160
+ * the caller-controlled wrapper used to discover it.
161
+ */
162
+ export declare function resolveTrustedPreferenceMemoryPromotionProducer(value: unknown): PreferenceMemoryPromotionProducer | undefined;
163
+ /** Process-local authenticity predicate shared by the producer and consumer. */
164
+ export declare function isTrustedPreferenceMemoryPromotionProducer(value: unknown): value is PreferenceMemoryPromotionProducer;
165
+ export declare function preferenceMemoryPromotionRequestDigest(value: unknown): string;
166
+ export declare function withPreferenceMemoryPromotionRequestDigest<T extends Readonly<Record<string, unknown>>>(value: T): Readonly<T & {
167
+ requestDigest: string;
168
+ }>;
169
+ export declare function validatePreferenceMemoryPromotionRequest(value: unknown): Readonly<PreferenceMemoryPromotionRequest>;
170
+ export declare function preferenceMemoryPromotionSubmissionDigest(value: unknown): string;
171
+ export declare function withPreferenceMemoryPromotionSubmissionDigest<T extends Readonly<Record<string, unknown>>>(value: T): Readonly<T & {
172
+ receiptDigest: string;
173
+ }>;
174
+ export declare function validatePreferenceMemoryPromotionSubmissionReceipt(value: unknown, expected?: Pick<PreferenceMemoryPromotionRequest, 'promotionId' | 'promotionGeneration' | 'requestDigest'>): Readonly<PreferenceMemoryPromotionSubmissionReceipt>;
175
+ export declare function preferenceMemoryPromotionResultDigest(value: unknown): string;
176
+ export declare function withPreferenceMemoryPromotionResultDigest<T extends Readonly<Record<string, unknown>>>(value: T): Readonly<T & {
177
+ receiptDigest: string;
178
+ }>;
179
+ export declare function validatePreferenceMemoryPromotionResult(value: unknown, expected?: Pick<PreferenceMemoryPromotionRequest, 'promotionId' | 'promotionGeneration' | 'requestDigest'>): Readonly<PreferenceMemoryPromotionResult>;
180
+ export declare function validatePreferenceMemoryPromotionResultAck(value: unknown): Readonly<PreferenceMemoryPromotionResultAck>;
181
+ export declare function preferenceMemoryPromotionCancellationDigest(value: unknown): string;
182
+ export declare function withPreferenceMemoryPromotionCancellationDigest<T extends Readonly<Record<string, unknown>>>(value: T): Readonly<T & {
183
+ cancellationDigest: string;
184
+ }>;
185
+ export declare function validatePreferenceMemoryPromotionCancellationRequest(value: unknown): Readonly<PreferenceMemoryPromotionCancellationRequest>;
186
+ export declare function preferenceMemoryPromotionCancellationReceiptDigest(value: unknown): string;
187
+ export declare function withPreferenceMemoryPromotionCancellationReceiptDigest<T extends Readonly<Record<string, unknown>>>(value: T): Readonly<T & {
188
+ receiptDigest: string;
189
+ }>;
190
+ export declare function validatePreferenceMemoryPromotionCancellationReceipt(value: unknown, expected: Readonly<PreferenceMemoryPromotionCancellationRequest>): Readonly<PreferenceMemoryPromotionCancellationReceipt>;
191
+ export {};
192
+ //# sourceMappingURL=promotion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promotion.d.ts","sourceRoot":"","sources":["../src/promotion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,442 @@
1
+ import { isAbsolute, resolve } from 'node:path';
2
+ import { AssistantGrowthContractError, assertExactGrowthKeys, exactGrowthDigest, growthObjectDigest, isGrowthRecord, } from './canonical.js';
3
+ /** Private Host protocol between Preference Learning and Personal Memory. */
4
+ export const PREFERENCE_MEMORY_PROMOTION_PROTOCOL = 'assistant-growth/preference-memory-promotion/v1';
5
+ export const PREFERENCE_MEMORY_PROMOTION_REQUEST_PROTOCOL = 'assistant-growth/preference-memory-promotion-request/v1';
6
+ export const PREFERENCE_MEMORY_PROMOTION_SUBMISSION_PROTOCOL = 'assistant-growth/preference-memory-promotion-submission/v1';
7
+ export const PREFERENCE_MEMORY_PROMOTION_RESULT_PROTOCOL = 'assistant-growth/preference-memory-promotion-result/v1';
8
+ export const PREFERENCE_MEMORY_PROMOTION_CANCELLATION_PROTOCOL = 'assistant-growth/preference-memory-promotion-cancellation/v1';
9
+ /** The first release deliberately supports exactly one low-sensitivity T2 pair. */
10
+ export const PREFERENCE_MEMORY_PROMOTION_KEY = 'memory.retention';
11
+ export const PREFERENCE_MEMORY_PROMOTION_VALUE = 'long-term';
12
+ export const PREFERENCE_MEMORY_PROMOTION_RENDERER_ID = 'memory.retention.long-term/v1';
13
+ export const PREFERENCE_MEMORY_PROMOTION_CONTENT = 'Retain information the owner explicitly confirms for long-term memory.';
14
+ const trustedPreferenceMemoryPromotionProducers = new WeakSet();
15
+ const preferenceMemoryPromotionProducerProbe = Symbol('assistant-growth-contract.preference-memory-promotion-producer-probe');
16
+ /**
17
+ * Marks the exact live Preference producer owned by the producer package. The
18
+ * private probe lets Cordis wrappers resolve back to that exact object without
19
+ * making a structural lookalike authentic. This is a process-local composition
20
+ * guard, not a sandbox boundary against already-trusted Host code.
21
+ */
22
+ export function brandPreferenceMemoryPromotionProducer(producer) {
23
+ if (typeof producer !== 'object' || producer === null) {
24
+ throw new TypeError('preference Memory promotion producer must be an object');
25
+ }
26
+ const exact = producer;
27
+ const current = Object.getOwnPropertyDescriptor(exact, preferenceMemoryPromotionProducerProbe);
28
+ if (current === undefined) {
29
+ Object.defineProperty(exact, preferenceMemoryPromotionProducerProbe, {
30
+ value: () => exact,
31
+ enumerable: false,
32
+ configurable: false,
33
+ writable: false,
34
+ });
35
+ }
36
+ else if (typeof current.value !== 'function' || current.value() !== exact) {
37
+ throw new TypeError('preference Memory promotion producer brand is invalid');
38
+ }
39
+ trustedPreferenceMemoryPromotionProducers.add(exact);
40
+ }
41
+ /** Revokes the exact producer brand when its owning service is disposed. */
42
+ export function unbrandPreferenceMemoryPromotionProducer(producer) {
43
+ if (typeof producer === 'object' && producer !== null) {
44
+ trustedPreferenceMemoryPromotionProducers.delete(producer);
45
+ }
46
+ }
47
+ /**
48
+ * Resolves a Cordis forwarding wrapper to the exact producer object that owns
49
+ * the process-local brand. Consumers must bind and invoke this object, never
50
+ * the caller-controlled wrapper used to discover it.
51
+ */
52
+ export function resolveTrustedPreferenceMemoryPromotionProducer(value) {
53
+ if (typeof value !== 'object' || value === null)
54
+ return undefined;
55
+ try {
56
+ const probe = value[preferenceMemoryPromotionProducerProbe];
57
+ if (typeof probe !== 'function')
58
+ return undefined;
59
+ const exact = probe();
60
+ if (typeof exact !== 'object' || exact === null
61
+ || !trustedPreferenceMemoryPromotionProducers.has(exact))
62
+ return undefined;
63
+ return exact;
64
+ }
65
+ catch {
66
+ return undefined;
67
+ }
68
+ }
69
+ /** Process-local authenticity predicate shared by the producer and consumer. */
70
+ export function isTrustedPreferenceMemoryPromotionProducer(value) {
71
+ return resolveTrustedPreferenceMemoryPromotionProducer(value) !== undefined;
72
+ }
73
+ const identifierPattern = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,999}$/u;
74
+ const presetPattern = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,199}$/u;
75
+ function invalid(message) {
76
+ throw new AssistantGrowthContractError('invalid-input', message);
77
+ }
78
+ function identifier(value, label, maxBytes = 1_000) {
79
+ if (typeof value !== 'string' || value.normalize('NFC').trim() !== value || value === ''
80
+ || Buffer.byteLength(value, 'utf8') > maxBytes || !identifierPattern.test(value)) {
81
+ invalid(`${label} is invalid`);
82
+ }
83
+ return value;
84
+ }
85
+ function canonicalPrincipal(value) {
86
+ if (typeof value !== 'string' || value.normalize('NFC').trim() !== value || value === ''
87
+ || Buffer.byteLength(value, 'utf8') > 4_096
88
+ || [...value].some(character => {
89
+ const codePoint = character.codePointAt(0);
90
+ return codePoint <= 0x1f || codePoint === 0x7f;
91
+ })) {
92
+ invalid('promotion principalId is invalid');
93
+ }
94
+ return value;
95
+ }
96
+ function positiveInteger(value, label) {
97
+ if (!Number.isSafeInteger(value) || value < 1)
98
+ invalid(`${label} is invalid`);
99
+ return value;
100
+ }
101
+ function nonNegativeInteger(value, label) {
102
+ if (!Number.isSafeInteger(value) || value < 0)
103
+ invalid(`${label} is invalid`);
104
+ return value;
105
+ }
106
+ function basisPoints(value, label) {
107
+ const normalized = nonNegativeInteger(value, label);
108
+ if (normalized > 10_000)
109
+ invalid(`${label} is invalid`);
110
+ return normalized;
111
+ }
112
+ function validateScope(value) {
113
+ if (!isGrowthRecord(value))
114
+ invalid('promotion scope must be an object');
115
+ assertExactGrowthKeys(value, ['workspace', 'preset'], 'promotion scope');
116
+ if (typeof value['workspace'] !== 'string' || !isAbsolute(value['workspace'])
117
+ || resolve(value['workspace']) !== value['workspace']) {
118
+ invalid('promotion workspace is invalid');
119
+ }
120
+ const preset = identifier(value['preset'], 'promotion preset', 200);
121
+ if (!presetPattern.test(preset))
122
+ invalid('promotion preset is invalid');
123
+ return Object.freeze({ workspace: value['workspace'], preset });
124
+ }
125
+ function validateLineage(value) {
126
+ if (!isGrowthRecord(value))
127
+ invalid('promotion principal lineage must be an object');
128
+ assertExactGrowthKeys(value, ['principalRecordId', 'principalVersion'], 'promotion principal lineage');
129
+ return Object.freeze({
130
+ principalRecordId: identifier(value['principalRecordId'], 'principalRecordId'),
131
+ principalVersion: positiveInteger(value['principalVersion'], 'principalVersion'),
132
+ });
133
+ }
134
+ function validateHypothesis(value) {
135
+ if (!isGrowthRecord(value))
136
+ invalid('promotion hypothesis must be an object');
137
+ assertExactGrowthKeys(value, [
138
+ 'id', 'key', 'value', 'version', 'confidenceBps', 'contradictionBps',
139
+ 'supportingSignals', 'distinctSignalSources', 'evidenceMass',
140
+ ], 'promotion hypothesis');
141
+ if (value['key'] !== PREFERENCE_MEMORY_PROMOTION_KEY
142
+ || value['value'] !== PREFERENCE_MEMORY_PROMOTION_VALUE) {
143
+ invalid('promotion hypothesis is outside the fixed allowlist');
144
+ }
145
+ return Object.freeze({
146
+ id: identifier(value['id'], 'hypothesis id'),
147
+ key: PREFERENCE_MEMORY_PROMOTION_KEY,
148
+ value: PREFERENCE_MEMORY_PROMOTION_VALUE,
149
+ version: positiveInteger(value['version'], 'hypothesis version'),
150
+ confidenceBps: basisPoints(value['confidenceBps'], 'hypothesis confidenceBps'),
151
+ contradictionBps: basisPoints(value['contradictionBps'], 'hypothesis contradictionBps'),
152
+ supportingSignals: positiveInteger(value['supportingSignals'], 'hypothesis supportingSignals'),
153
+ distinctSignalSources: positiveInteger(value['distinctSignalSources'], 'hypothesis distinctSignalSources'),
154
+ evidenceMass: positiveInteger(value['evidenceMass'], 'hypothesis evidenceMass'),
155
+ });
156
+ }
157
+ function requestPayload(value) {
158
+ return Object.fromEntries(Object.entries(value).filter(([key]) => key !== 'requestDigest'));
159
+ }
160
+ export function preferenceMemoryPromotionRequestDigest(value) {
161
+ if (!isGrowthRecord(value))
162
+ invalid('promotion request digest input must be an object');
163
+ return growthObjectDigest({
164
+ contract: PREFERENCE_MEMORY_PROMOTION_REQUEST_PROTOCOL,
165
+ request: requestPayload(value),
166
+ });
167
+ }
168
+ export function withPreferenceMemoryPromotionRequestDigest(value) {
169
+ if (Object.hasOwn(value, 'requestDigest'))
170
+ invalid('promotion request already contains requestDigest');
171
+ return Object.freeze({ ...value, requestDigest: preferenceMemoryPromotionRequestDigest(value) });
172
+ }
173
+ export function validatePreferenceMemoryPromotionRequest(value) {
174
+ if (!isGrowthRecord(value))
175
+ invalid('preference Memory promotion request must be an object');
176
+ assertExactGrowthKeys(value, [
177
+ 'contractVersion', 'promotionId', 'promotionGeneration', 'requestDigest',
178
+ 'idempotencyKey', 'scope', 'principalId', 'principalLineage', 'ownerGeneration',
179
+ 'hypothesis', 'rendererId', 'observedAt', 'deadlineAt',
180
+ ], 'preference Memory promotion request');
181
+ if (value['contractVersion'] !== 1
182
+ || value['rendererId'] !== PREFERENCE_MEMORY_PROMOTION_RENDERER_ID) {
183
+ invalid('preference Memory promotion contract or renderer is invalid');
184
+ }
185
+ const request = Object.freeze({
186
+ contractVersion: 1,
187
+ promotionId: identifier(value['promotionId'], 'promotionId'),
188
+ promotionGeneration: positiveInteger(value['promotionGeneration'], 'promotionGeneration'),
189
+ requestDigest: exactGrowthDigest(value['requestDigest'], 'requestDigest'),
190
+ idempotencyKey: identifier(value['idempotencyKey'], 'idempotencyKey'),
191
+ scope: validateScope(value['scope']),
192
+ principalId: canonicalPrincipal(value['principalId']),
193
+ principalLineage: validateLineage(value['principalLineage']),
194
+ ownerGeneration: positiveInteger(value['ownerGeneration'], 'ownerGeneration'),
195
+ hypothesis: validateHypothesis(value['hypothesis']),
196
+ rendererId: PREFERENCE_MEMORY_PROMOTION_RENDERER_ID,
197
+ observedAt: nonNegativeInteger(value['observedAt'], 'observedAt'),
198
+ deadlineAt: nonNegativeInteger(value['deadlineAt'], 'deadlineAt'),
199
+ });
200
+ if (request.deadlineAt <= request.observedAt)
201
+ invalid('promotion deadline must follow observedAt');
202
+ if (preferenceMemoryPromotionRequestDigest(request) !== request.requestDigest) {
203
+ throw new AssistantGrowthContractError('receipt-mismatch', 'promotion request digest is stale');
204
+ }
205
+ return request;
206
+ }
207
+ function receiptPayload(value) {
208
+ return Object.fromEntries(Object.entries(value).filter(([key]) => key !== 'receiptDigest'));
209
+ }
210
+ function receiptDigest(protocol, value) {
211
+ return growthObjectDigest({ contract: protocol, receipt: receiptPayload(value) });
212
+ }
213
+ export function preferenceMemoryPromotionSubmissionDigest(value) {
214
+ if (!isGrowthRecord(value))
215
+ invalid('promotion submission receipt must be an object');
216
+ return receiptDigest(PREFERENCE_MEMORY_PROMOTION_SUBMISSION_PROTOCOL, value);
217
+ }
218
+ export function withPreferenceMemoryPromotionSubmissionDigest(value) {
219
+ if (Object.hasOwn(value, 'receiptDigest'))
220
+ invalid('promotion submission already contains receiptDigest');
221
+ return Object.freeze({ ...value, receiptDigest: preferenceMemoryPromotionSubmissionDigest(value) });
222
+ }
223
+ function assertResultIdentity(value, expected) {
224
+ const identity = {
225
+ promotionId: identifier(value['promotionId'], 'promotionId'),
226
+ promotionGeneration: positiveInteger(value['promotionGeneration'], 'promotionGeneration'),
227
+ requestDigest: exactGrowthDigest(value['requestDigest'], 'requestDigest'),
228
+ };
229
+ if (expected !== undefined && (identity.promotionId !== expected.promotionId
230
+ || identity.promotionGeneration !== expected.promotionGeneration
231
+ || identity.requestDigest !== expected.requestDigest)) {
232
+ throw new AssistantGrowthContractError('receipt-mismatch', 'promotion receipt changed request identity');
233
+ }
234
+ return identity;
235
+ }
236
+ export function validatePreferenceMemoryPromotionSubmissionReceipt(value, expected) {
237
+ if (!isGrowthRecord(value))
238
+ invalid('promotion submission receipt must be an object');
239
+ assertExactGrowthKeys(value, [
240
+ 'contractVersion', 'promotionId', 'promotionGeneration', 'requestDigest',
241
+ 'outcome', 'memoryProposalId', 'receiptDigest',
242
+ ], 'promotion submission receipt');
243
+ if (value['contractVersion'] !== 1
244
+ || (value['outcome'] !== 'accepted' && value['outcome'] !== 'replayed')) {
245
+ invalid('promotion submission receipt is invalid');
246
+ }
247
+ const result = Object.freeze({
248
+ contractVersion: 1,
249
+ ...assertResultIdentity(value, expected),
250
+ outcome: value['outcome'],
251
+ memoryProposalId: identifier(value['memoryProposalId'], 'memoryProposalId'),
252
+ receiptDigest: exactGrowthDigest(value['receiptDigest'], 'receiptDigest'),
253
+ });
254
+ if (preferenceMemoryPromotionSubmissionDigest(result) !== result.receiptDigest) {
255
+ throw new AssistantGrowthContractError('receipt-mismatch', 'promotion submission receipt digest is stale');
256
+ }
257
+ return result;
258
+ }
259
+ export function preferenceMemoryPromotionResultDigest(value) {
260
+ if (!isGrowthRecord(value))
261
+ invalid('promotion result must be an object');
262
+ return receiptDigest(PREFERENCE_MEMORY_PROMOTION_RESULT_PROTOCOL, value);
263
+ }
264
+ export function withPreferenceMemoryPromotionResultDigest(value) {
265
+ if (Object.hasOwn(value, 'receiptDigest'))
266
+ invalid('promotion result already contains receiptDigest');
267
+ return Object.freeze({ ...value, receiptDigest: preferenceMemoryPromotionResultDigest(value) });
268
+ }
269
+ export function validatePreferenceMemoryPromotionResult(value, expected) {
270
+ if (!isGrowthRecord(value))
271
+ invalid('preference Memory promotion result must be an object');
272
+ const baseKeys = [
273
+ 'contractVersion', 'promotionId', 'promotionGeneration', 'requestDigest',
274
+ 'resultVersion', 'status', 'occurredAt', 'receiptDigest',
275
+ ];
276
+ const status = value['status'];
277
+ if (status === 'confirmed') {
278
+ assertExactGrowthKeys(value, [...baseKeys, 'memoryProposalId', 'memoryProposalVersion',
279
+ 'memoryRecordId', 'memoryRecordVersion', 'memoryRecordDigest'], 'promotion result');
280
+ }
281
+ else if (status === 'rejected') {
282
+ assertExactGrowthKeys(value, [...baseKeys, 'rejectionKind', 'memoryProposalId',
283
+ 'memoryProposalVersion'], 'promotion result');
284
+ if (value['rejectionKind'] !== 'owner-explicit') {
285
+ invalid('rejected promotion result lacks explicit owner rejection');
286
+ }
287
+ }
288
+ else if (status === 'expired' || status === 'conflicted') {
289
+ assertExactGrowthKeys(value, [...baseKeys, 'memoryProposalId', 'memoryProposalVersion'], 'promotion result');
290
+ }
291
+ else if (status === 'stale-owner') {
292
+ assertExactGrowthKeys(value, baseKeys, 'promotion result');
293
+ }
294
+ else {
295
+ invalid('promotion terminal status is invalid');
296
+ }
297
+ if (value['contractVersion'] !== 1)
298
+ invalid('promotion result contract version is invalid');
299
+ const common = {
300
+ contractVersion: 1,
301
+ ...assertResultIdentity(value, expected),
302
+ resultVersion: positiveInteger(value['resultVersion'], 'resultVersion'),
303
+ occurredAt: nonNegativeInteger(value['occurredAt'], 'occurredAt'),
304
+ receiptDigest: exactGrowthDigest(value['receiptDigest'], 'receiptDigest'),
305
+ };
306
+ let result;
307
+ if (status === 'confirmed') {
308
+ result = Object.freeze({
309
+ ...common, status,
310
+ memoryProposalId: identifier(value['memoryProposalId'], 'memoryProposalId'),
311
+ memoryProposalVersion: positiveInteger(value['memoryProposalVersion'], 'memoryProposalVersion'),
312
+ memoryRecordId: identifier(value['memoryRecordId'], 'memoryRecordId'),
313
+ memoryRecordVersion: positiveInteger(value['memoryRecordVersion'], 'memoryRecordVersion'),
314
+ memoryRecordDigest: exactGrowthDigest(value['memoryRecordDigest'], 'memoryRecordDigest'),
315
+ });
316
+ }
317
+ else if (status === 'rejected') {
318
+ result = Object.freeze({
319
+ ...common, status, rejectionKind: 'owner-explicit',
320
+ memoryProposalId: identifier(value['memoryProposalId'], 'memoryProposalId'),
321
+ memoryProposalVersion: positiveInteger(value['memoryProposalVersion'], 'memoryProposalVersion'),
322
+ });
323
+ }
324
+ else if (status === 'expired' || status === 'conflicted') {
325
+ result = Object.freeze({
326
+ ...common, status,
327
+ memoryProposalId: identifier(value['memoryProposalId'], 'memoryProposalId'),
328
+ memoryProposalVersion: positiveInteger(value['memoryProposalVersion'], 'memoryProposalVersion'),
329
+ });
330
+ }
331
+ else {
332
+ result = Object.freeze({ ...common, status: 'stale-owner' });
333
+ }
334
+ if (preferenceMemoryPromotionResultDigest(result) !== result.receiptDigest) {
335
+ throw new AssistantGrowthContractError('receipt-mismatch', 'promotion result digest is stale');
336
+ }
337
+ return result;
338
+ }
339
+ export function validatePreferenceMemoryPromotionResultAck(value) {
340
+ if (!isGrowthRecord(value))
341
+ invalid('promotion result acknowledgement must be an object');
342
+ assertExactGrowthKeys(value, [
343
+ 'contractVersion', 'promotionId', 'promotionGeneration', 'resultVersion',
344
+ 'receiptDigest', 'outcome',
345
+ ], 'promotion result acknowledgement');
346
+ if (value['contractVersion'] !== 1
347
+ || !['applied', 'replayed', 'cancelled'].includes(String(value['outcome']))) {
348
+ invalid('promotion result acknowledgement is invalid');
349
+ }
350
+ return Object.freeze({
351
+ contractVersion: 1,
352
+ promotionId: identifier(value['promotionId'], 'promotionId'),
353
+ promotionGeneration: positiveInteger(value['promotionGeneration'], 'promotionGeneration'),
354
+ resultVersion: positiveInteger(value['resultVersion'], 'resultVersion'),
355
+ receiptDigest: exactGrowthDigest(value['receiptDigest'], 'receiptDigest'),
356
+ outcome: value['outcome'],
357
+ });
358
+ }
359
+ export function preferenceMemoryPromotionCancellationDigest(value) {
360
+ if (!isGrowthRecord(value))
361
+ invalid('promotion cancellation request must be an object');
362
+ const payload = Object.fromEntries(Object.entries(value).filter(([key]) => key !== 'cancellationDigest'));
363
+ return growthObjectDigest({ contract: PREFERENCE_MEMORY_PROMOTION_CANCELLATION_PROTOCOL, request: payload });
364
+ }
365
+ export function withPreferenceMemoryPromotionCancellationDigest(value) {
366
+ if (Object.hasOwn(value, 'cancellationDigest'))
367
+ invalid('cancellation already contains its digest');
368
+ return Object.freeze({ ...value, cancellationDigest: preferenceMemoryPromotionCancellationDigest(value) });
369
+ }
370
+ export function validatePreferenceMemoryPromotionCancellationRequest(value) {
371
+ if (!isGrowthRecord(value))
372
+ invalid('promotion cancellation request must be an object');
373
+ assertExactGrowthKeys(value, [
374
+ 'contractVersion', 'promotionId', 'promotionGeneration', 'requestDigest',
375
+ 'principalLineage', 'ownerGeneration', 'reason', 'occurredAt', 'cancellationDigest',
376
+ ], 'promotion cancellation request');
377
+ if (value['contractVersion'] !== 1
378
+ || !['forget', 'owner-rotated', 'superseded']
379
+ .includes(String(value['reason']))) {
380
+ invalid('promotion cancellation request is invalid');
381
+ }
382
+ const result = Object.freeze({
383
+ contractVersion: 1,
384
+ promotionId: identifier(value['promotionId'], 'promotionId'),
385
+ promotionGeneration: positiveInteger(value['promotionGeneration'], 'promotionGeneration'),
386
+ requestDigest: exactGrowthDigest(value['requestDigest'], 'requestDigest'),
387
+ principalLineage: validateLineage(value['principalLineage']),
388
+ ownerGeneration: positiveInteger(value['ownerGeneration'], 'ownerGeneration'),
389
+ reason: value['reason'],
390
+ occurredAt: nonNegativeInteger(value['occurredAt'], 'occurredAt'),
391
+ cancellationDigest: exactGrowthDigest(value['cancellationDigest'], 'cancellationDigest'),
392
+ });
393
+ if (preferenceMemoryPromotionCancellationDigest(result) !== result.cancellationDigest) {
394
+ throw new AssistantGrowthContractError('receipt-mismatch', 'promotion cancellation digest is stale');
395
+ }
396
+ return result;
397
+ }
398
+ export function preferenceMemoryPromotionCancellationReceiptDigest(value) {
399
+ if (!isGrowthRecord(value))
400
+ invalid('promotion cancellation receipt must be an object');
401
+ return receiptDigest(PREFERENCE_MEMORY_PROMOTION_CANCELLATION_PROTOCOL, value);
402
+ }
403
+ export function withPreferenceMemoryPromotionCancellationReceiptDigest(value) {
404
+ if (Object.hasOwn(value, 'receiptDigest'))
405
+ invalid('cancellation receipt already contains receiptDigest');
406
+ return Object.freeze({ ...value, receiptDigest: preferenceMemoryPromotionCancellationReceiptDigest(value) });
407
+ }
408
+ export function validatePreferenceMemoryPromotionCancellationReceipt(value, expected) {
409
+ if (!isGrowthRecord(value))
410
+ invalid('promotion cancellation receipt must be an object');
411
+ assertExactGrowthKeys(value, [
412
+ 'contractVersion', 'promotionId', 'promotionGeneration', 'requestDigest',
413
+ 'cancellationDigest', 'outcome', 'receiptDigest',
414
+ ], 'promotion cancellation receipt');
415
+ if (value['contractVersion'] !== 1
416
+ || !['cancelled', 'replayed', 'already-confirmed'].includes(String(value['outcome']))) {
417
+ invalid('promotion cancellation receipt is invalid');
418
+ }
419
+ const result = Object.freeze({
420
+ contractVersion: 1,
421
+ promotionId: identifier(value['promotionId'], 'promotionId'),
422
+ promotionGeneration: positiveInteger(value['promotionGeneration'], 'promotionGeneration'),
423
+ requestDigest: exactGrowthDigest(value['requestDigest'], 'requestDigest'),
424
+ cancellationDigest: exactGrowthDigest(value['cancellationDigest'], 'cancellationDigest'),
425
+ outcome: value['outcome'],
426
+ receiptDigest: exactGrowthDigest(value['receiptDigest'], 'receiptDigest'),
427
+ });
428
+ if (result.promotionId !== expected.promotionId
429
+ || result.promotionGeneration !== expected.promotionGeneration
430
+ || result.requestDigest !== expected.requestDigest
431
+ || result.cancellationDigest !== expected.cancellationDigest) {
432
+ throw new AssistantGrowthContractError('receipt-mismatch', 'cancellation receipt changed request identity');
433
+ }
434
+ if (result.outcome === 'already-confirmed' && expected.reason !== 'superseded') {
435
+ throw new AssistantGrowthContractError('receipt-mismatch', 'privacy cancellation requires durable Memory compensation');
436
+ }
437
+ if (preferenceMemoryPromotionCancellationReceiptDigest(result) !== result.receiptDigest) {
438
+ throw new AssistantGrowthContractError('receipt-mismatch', 'promotion cancellation receipt digest is stale');
439
+ }
440
+ return result;
441
+ }
442
+ //# sourceMappingURL=promotion.js.map