@weiliang0121/capix-memory 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +18 -2
- package/dist/apply.d.ts +23 -0
- package/dist/apply.js +1 -0
- package/dist/change.d.ts +15 -0
- package/dist/change.js +2 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +2 -0
- package/dist/memories.d.ts +40 -0
- package/dist/memories.js +2 -0
- package/dist/memory.d.ts +23 -0
- package/dist/memory.js +2 -0
- package/dist/persistence.d.ts +23 -0
- package/dist/persistence.js +1 -0
- package/dist/resource.d.ts +36 -0
- package/dist/resource.js +2 -0
- package/dist/source.d.ts +3 -0
- package/dist/source.js +2 -0
- package/dist/validation.d.ts +4 -0
- package/dist/validation.js +2 -0
- package/dist/view.d.ts +23 -0
- package/dist/view.js +2 -0
- package/package.json +46 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 wei.liang (https://github.com/weiliang0121)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @weiliang0121/capix-memory
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Versioned memory entities and controlled resource access for CAPiX.
|
|
4
|
+
|
|
5
|
+
ES modules and TypeScript declarations. Requires Node.js 22.23.2 or newer.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @weiliang0121/capix-memory
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Public entry points
|
|
12
|
+
|
|
13
|
+
- `@weiliang0121/capix-memory`
|
|
14
|
+
|
|
15
|
+
This package is part of CAPiX. The npm distribution contains compiled JavaScript, type declarations, and this package's MIT license. Reference services and client applications are not included in this package or the initial npm release.
|
|
16
|
+
|
|
17
|
+
## License
|
|
18
|
+
|
|
19
|
+
MIT. Copyright (c) 2026 wei.liang. See [LICENSE](./LICENSE).
|
package/dist/apply.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { MemoryChange } from './change.js';
|
|
2
|
+
export interface MemoryFixedApply {
|
|
3
|
+
readonly contract: {
|
|
4
|
+
readonly key: 'memories.apply';
|
|
5
|
+
readonly version: 1;
|
|
6
|
+
};
|
|
7
|
+
readonly operation: {
|
|
8
|
+
readonly contextId: string;
|
|
9
|
+
readonly position: string;
|
|
10
|
+
};
|
|
11
|
+
readonly memoriesId: string;
|
|
12
|
+
readonly changes: readonly MemoryChange[];
|
|
13
|
+
}
|
|
14
|
+
export interface MemoryApplyReceipt {
|
|
15
|
+
readonly memoriesId: string;
|
|
16
|
+
readonly startSequence: number;
|
|
17
|
+
readonly sequence: number;
|
|
18
|
+
readonly memoryIds: readonly string[];
|
|
19
|
+
}
|
|
20
|
+
export interface MemoryApplyState {
|
|
21
|
+
readonly request: MemoryFixedApply;
|
|
22
|
+
readonly receipt: MemoryApplyReceipt;
|
|
23
|
+
}
|
package/dist/apply.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/*! MIT License | Copyright (c) 2026 wei.liang | See LICENSE */
|
package/dist/change.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MemoryContent, MemoryResultSource } from '@weiliang0121/capix-protocol';
|
|
2
|
+
import type { Transcript } from '@weiliang0121/capix-transcript';
|
|
3
|
+
export type MemoryChange = {
|
|
4
|
+
readonly change: string;
|
|
5
|
+
readonly sourceIndexes: readonly number[];
|
|
6
|
+
readonly sourceResults?: readonly MemoryResultSource[];
|
|
7
|
+
readonly result: MemoryContent;
|
|
8
|
+
} & ({
|
|
9
|
+
readonly action: 'create';
|
|
10
|
+
readonly memoryId: null;
|
|
11
|
+
} | {
|
|
12
|
+
readonly action: 'update';
|
|
13
|
+
readonly memoryId: string;
|
|
14
|
+
});
|
|
15
|
+
export declare function prepareMemoryChanges(transcript: Transcript, changes: readonly MemoryChange[]): readonly MemoryChange[];
|
package/dist/change.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! MIT License | Copyright (c) 2026 wei.liang | See LICENSE */
|
|
2
|
+
var __defProp=Object.defineProperty;var __name=(target,value)=>__defProp(target,"name",{value,configurable:!0});import{isEqual}from"es-toolkit/predicate";import{isArray}from"es-toolkit/compat";import{Memory}from"./memory.js";import{prepareMemoryResultSources}from"./source.js";import{hasUniqueItems,isNonBlankString,isNonEmptyArray,isObject}from"./validation.js";function prepareMemoryChanges(transcript,changes){if(!isArray(changes))throw new TypeError("Memory changes must be an array");return Object.freeze(Array.from(changes,change=>prepareMemoryChange(transcript,change)))}__name(prepareMemoryChanges,"prepareMemoryChanges");function prepareMemoryChange(transcript,value){if(!isObject(value))throw new TypeError("Memory change requires an explanation and unique evidence");const action=value.action,memoryId=value.memoryId,change=value.change,evidence=value.sourceIndexes;if(!isNonBlankString(change)||!isNonEmptyArray(evidence))throw new TypeError("Memory change requires an explanation and unique evidence");const sourceIndexes=Object.freeze([...evidence]);if(!isNonEmptyArray(sourceIndexes)||!hasUniqueItems(sourceIndexes))throw new TypeError("Memory change requires an explanation and unique evidence");for(const index of sourceIndexes)transcript.ref(index);const sourceResults=prepareMemoryResultSources(transcript,value.sourceResults),result=Memory.prepare(transcript,value.result);if(!sourceIndexes.every(index=>result.sourceIndexes.includes(index)))throw new Error("Memory result must include change evidence");if(sourceResults?.some(source=>!result.sourceResults?.some(candidate=>isEqual(source,candidate))))throw new Error("Memory result must include the unchanged result sources of this change");if(action==="create"){if(memoryId!==null)throw new Error("Creating memory cannot specify an existing identity");return Object.freeze({action,memoryId,change,sourceIndexes,...sourceResults?{sourceResults}:{},result})}if(action!=="update")throw new Error("Unknown memory change action");if(!isNonBlankString(memoryId))throw new TypeError("Memory update requires an identity");return Object.freeze({action,memoryId,change,sourceIndexes,...sourceResults?{sourceResults}:{},result})}__name(prepareMemoryChange,"prepareMemoryChange");export{prepareMemoryChanges};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { Memory } from './memory.js';
|
|
2
|
+
export { Memories, type MemoriesOptions } from './memories.js';
|
|
3
|
+
export type { MemoryChange } from './change.js';
|
|
4
|
+
export type { MemoryFixedApply, MemoryApplyReceipt, MemoryApplyState } from './apply.js';
|
|
5
|
+
export type { MemoryState, MemoriesState, MemoriesCommit, MemoriesPersistence } from './persistence.js';
|
|
6
|
+
export { MemoryResource, type CapturedMemories, type MemoryBasis, type MemoryApplication } from './resource.js';
|
|
7
|
+
export { MemoryViewSource, type MemoryViewSummary } from './view.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type ChangeProvenance, type EventConsumer, type ChangeCapture } from '@weiliang0121/capix-changes';
|
|
2
|
+
import type { MemoryEvent } from '@weiliang0121/capix-protocol';
|
|
3
|
+
import { Memory } from './memory.js';
|
|
4
|
+
import { type MemoryChange } from './change.js';
|
|
5
|
+
import type { Transcript } from '@weiliang0121/capix-transcript';
|
|
6
|
+
import type { MemoriesPersistence, MemoriesState } from './persistence.js';
|
|
7
|
+
import type { MemoryFixedApply, MemoryApplyReceipt } from './apply.js';
|
|
8
|
+
export interface MemoriesOptions {
|
|
9
|
+
observation?: EventConsumer<MemoryEvent>;
|
|
10
|
+
changes?: ChangeCapture<MemoryEvent>;
|
|
11
|
+
persistence?: MemoriesPersistence;
|
|
12
|
+
}
|
|
13
|
+
export declare class Memories {
|
|
14
|
+
#private;
|
|
15
|
+
readonly transcript: Transcript;
|
|
16
|
+
get id(): string;
|
|
17
|
+
get sequence(): number;
|
|
18
|
+
private readonly observation?;
|
|
19
|
+
private readonly changes?;
|
|
20
|
+
private readonly persistence?;
|
|
21
|
+
constructor(transcript: Transcript, options?: MemoriesOptions);
|
|
22
|
+
static restore(state: MemoriesState, transcript: Transcript, options?: MemoriesOptions): Memories;
|
|
23
|
+
exportState(): MemoriesState;
|
|
24
|
+
get(id: string): Memory | undefined;
|
|
25
|
+
values(): readonly Memory[];
|
|
26
|
+
snapshot(): {
|
|
27
|
+
topic: string;
|
|
28
|
+
content: string;
|
|
29
|
+
sourceIndexes: readonly number[];
|
|
30
|
+
sourceResults?: readonly import("@weiliang0121/capix-protocol").MemoryResultSource[];
|
|
31
|
+
id: string;
|
|
32
|
+
}[];
|
|
33
|
+
apply(changes: readonly MemoryChange[], provenance?: ChangeProvenance<MemoryEvent>): readonly Memory[];
|
|
34
|
+
confirmFixed(value: MemoryFixedApply): MemoryApplyReceipt | undefined;
|
|
35
|
+
applyFixed(value: MemoryFixedApply, provenance?: ChangeProvenance<MemoryEvent>): MemoryApplyReceipt;
|
|
36
|
+
private acceptFixed;
|
|
37
|
+
private confirmAccepted;
|
|
38
|
+
private applyPrepared;
|
|
39
|
+
private commit;
|
|
40
|
+
}
|
package/dist/memories.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! MIT License | Copyright (c) 2026 wei.liang | See LICENSE */
|
|
2
|
+
var __defProp=Object.defineProperty;var __name=(target,value)=>__defProp(target,"name",{value,configurable:!0});import{publishFact,combineFaults}from"@weiliang0121/capix-changes";import{randomUUID}from"node:crypto";import{isEqual}from"es-toolkit/predicate";import{Memory,restoreMemory,updateMemory}from"./memory.js";import{prepareMemoryChanges}from"./change.js";import{isNonBlankString,hasUniqueItems,isObject}from"./validation.js";import{isArray,isSafeInteger}from"es-toolkit/compat";import{RuntimeError}from"@weiliang0121/capix-contracts";import{PersistenceError}from"@weiliang0121/capix-diagnostics";class Memories{static{__name(this,"Memories")}transcript;#id=randomUUID();#sequence=0;#mutating=!1;get id(){return this.#id}get sequence(){return this.#sequence}#items=new Map;#applies=new Map;observation;changes;persistence;constructor(transcript,options={}){this.transcript=transcript,this.observation=options.observation,this.changes=options.changes,this.persistence=options.persistence}static restore(state,transcript,options={}){if(!isObject(state)||!isNonBlankString(state.id)||state.transcriptId!==transcript.id||!isArray(state.items))throw new TypeError("Invalid persisted memory collection");if(!isSafeInteger(state.sequence)||state.sequence<state.items.length||!state.items.length&&state.sequence!==0)throw new TypeError("Invalid persisted memory sequence");const restored=new Memories(transcript,options);for(const item of state.items){const memory=restoreMemory(transcript,item);if(restored.#items.has(memory.id))throw new TypeError("Duplicate persisted memory identity");restored.#items.set(memory.id,memory)}if(restored.#id=state.id,restored.#sequence=state.sequence,state.applies!==void 0){if(!isArray(state.applies))throw new TypeError("Invalid persisted memory applies");let previousSequence=0;for(const saved of state.applies){const request=restored.acceptFixed(saved.request),receipt=saved.receipt;if(!isObject(receipt)||receipt.memoriesId!==restored.id||!isSafeInteger(receipt.startSequence)||receipt.startSequence<previousSequence||!isSafeInteger(receipt.sequence)||receipt.sequence<receipt.startSequence||receipt.sequence>state.sequence||!isArray(receipt.memoryIds)||!hasUniqueItems(receipt.memoryIds)||receipt.memoryIds.length!==receipt.sequence-receipt.startSequence||receipt.memoryIds.some(id=>!restored.#items.has(id)))throw new TypeError("Invalid persisted memory apply receipt");const key=applyKey(request);if(restored.#applies.has(key))throw new TypeError("Duplicate persisted memory apply operation");restored.#applies.set(key,Object.freeze({request,receipt:Object.freeze({memoriesId:restored.id,startSequence:receipt.startSequence,sequence:receipt.sequence,memoryIds:Object.freeze([...receipt.memoryIds])})})),previousSequence=receipt.sequence}}return restored}exportState(){return Object.freeze({id:this.id,transcriptId:this.transcript.id,sequence:this.#sequence,items:Object.freeze(this.values().map(memory=>Object.freeze(memory.toJSON()))),...this.#applies.size?{applies:Object.freeze([...this.#applies.values()])}:{}})}get(id){return this.#items.get(id)}values(){return[...this.#items.values()]}snapshot(){return this.values().map(memory=>memory.toJSON())}apply(changes,provenance={}){if(this.#mutating)throw new Error("Memory persistence or change publication cannot reenter mutation");this.#mutating=!0;try{return this.applyPrepared(prepareMemoryChanges(this.transcript,changes),provenance)}finally{this.#mutating=!1}}confirmFixed(value){return this.confirmAccepted(this.acceptFixed(value))}applyFixed(value,provenance={}){if(this.#mutating)throw new Error("Memory persistence or change publication cannot reenter mutation");this.#mutating=!0;try{const request=this.acceptFixed(value),receipt=this.confirmAccepted(request);return receipt||(this.applyPrepared(request.changes,provenance,request),this.#applies.get(applyKey(request)).receipt)}finally{this.#mutating=!1}}acceptFixed(value){if(!isObject(value))throw new TypeError("Invalid fixed memory apply request");const contract=value.contract,origin=value.operation;if(contract?.key!=="memories.apply"||contract.version!==1||value.memoriesId!==this.id||!isObject(origin))throw new TypeError("Invalid fixed memory apply request");const operation=Object.freeze({contextId:origin.contextId,position:origin.position});if(!isNonBlankString(operation.contextId)||!isNonBlankString(operation.position))throw new TypeError("Invalid fixed memory apply request");const changes=prepareMemoryChanges(this.transcript,value.changes);return Object.freeze({contract:Object.freeze({key:"memories.apply",version:1}),operation,memoriesId:this.id,changes})}confirmAccepted(request){const saved=this.#applies.get(applyKey(request));if(saved&&!isEqual(saved.request,request))throw new RuntimeError("MEMORY_APPLY_CONFLICT","\u56FA\u5B9A\u8BB0\u5FC6\u6279\u6B21\u4E0E\u539F\u64CD\u4F5C\u4E0D\u4E00\u81F4\u3002");return saved?.receipt}applyPrepared(changes,provenance,request){const targets=new Set,prepared=changes.map(change=>{const result=change.result,sourceResults=change.sourceResults;if(change.action==="create")return{change,result,sourceResults,memory:void 0};const memory=this.#items.get(change.memoryId);if(!memory)throw new Error("Memory update target does not exist in this Runtime");for(const source of result.sourceResults??[]){const previous=memory.sourceResults?.find(candidate=>candidate.id===source.id);if(previous&&!isEqual(previous,source))throw new Error("Memory result source identity cannot change its receipt")}if(targets.has(memory.id))throw new Error("A batch cannot update the same memory twice");return targets.add(memory.id),{change,result,sourceResults,memory}}),mutations=[];for(const{change,result,sourceResults,memory:previous}of prepared){if(previous&&isEqual(previous.snapshot(),result))continue;const memory=previous??new Memory(this.transcript,result),sequence2=this.#sequence+mutations.length+1;if(!isSafeInteger(sequence2))throw new RangeError("Memory sequence is exhausted");const event=Object.freeze({type:"memory",memoriesId:this.id,sequence:sequence2,occurredAt:new Date().toISOString(),...provenance.origin?{origin:Object.freeze({...provenance.origin})}:{},change:Object.freeze({action:change.action,memoryId:memory.id,explanation:change.change,sourceIndexes:Object.freeze([...change.sourceIndexes]),...sourceResults?{sourceResults}:{},result})});mutations.push({memory,event,created:!previous})}if(!mutations.length&&!request)return[];const sequence=mutations.at(-1)?.event.sequence??this.#sequence,upserts=Object.freeze(mutations.map(({memory,event})=>Object.freeze({id:memory.id,...event.change.result}))),fixed=request?Object.freeze({request,receipt:Object.freeze({memoriesId:this.id,startSequence:this.#sequence,sequence,memoryIds:Object.freeze(upserts.map(memory=>memory.id))})}):void 0;this.commit(Object.freeze({memoriesId:this.id,transcriptId:this.transcript.id,expectedSequence:this.#sequence,sequence,upserts,...fixed?{fixed}:{}}));for(const{memory,event,created}of mutations)created?this.#items.set(memory.id,memory):updateMemory(memory,event.change.result);this.#sequence=sequence,fixed&&this.#applies.set(applyKey(fixed.request),fixed);const faults=[];for(const{event}of mutations)try{publishFact(event,this.changes,provenance.observation??this.observation)}catch(error){faults.push(error)}const fault=combineFaults(faults);if(fault!==void 0)throw fault;return mutations.map(({memory})=>memory)}commit(change){try{const result=this.persistence?.commit(change);if(result!==void 0)throw Promise.resolve(result).catch(()=>{}),new TypeError("Memory persistence must complete synchronously and return undefined")}catch(cause){throw cause instanceof PersistenceError?cause:new PersistenceError("memories.commit",cause)}}}function applyKey(request){return JSON.stringify([request.operation.contextId,request.operation.position])}__name(applyKey,"applyKey");export{Memories};
|
package/dist/memory.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { MemoryContent } from '@weiliang0121/capix-protocol';
|
|
2
|
+
import type { Transcript } from '@weiliang0121/capix-transcript';
|
|
3
|
+
import type { MemoryState } from './persistence.js';
|
|
4
|
+
export declare class Memory {
|
|
5
|
+
readonly transcript: Transcript;
|
|
6
|
+
constructor(transcript: Transcript, content: MemoryContent);
|
|
7
|
+
get id(): string;
|
|
8
|
+
get topic(): string;
|
|
9
|
+
get content(): string;
|
|
10
|
+
get sourceIndexes(): readonly number[];
|
|
11
|
+
get sourceResults(): readonly import("@weiliang0121/capix-protocol").MemoryResultSource[] | undefined;
|
|
12
|
+
snapshot(): MemoryContent;
|
|
13
|
+
toJSON(): {
|
|
14
|
+
topic: string;
|
|
15
|
+
content: string;
|
|
16
|
+
sourceIndexes: readonly number[];
|
|
17
|
+
sourceResults?: readonly import("@weiliang0121/capix-protocol").MemoryResultSource[];
|
|
18
|
+
id: string;
|
|
19
|
+
};
|
|
20
|
+
static prepare(transcript: Transcript, content: MemoryContent): MemoryContent;
|
|
21
|
+
}
|
|
22
|
+
export declare function updateMemory(memory: Memory, content: MemoryContent): void;
|
|
23
|
+
export declare function restoreMemory(transcript: Transcript, state: MemoryState): Memory;
|
package/dist/memory.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! MIT License | Copyright (c) 2026 wei.liang | See LICENSE */
|
|
2
|
+
var __defProp=Object.defineProperty;var __name=(target,value)=>__defProp(target,"name",{value,configurable:!0});import{randomUUID}from"node:crypto";import{isNonBlankString,isNonEmptyArray,hasUniqueItems,isObject}from"./validation.js";import{prepareMemoryResultSources}from"./source.js";const contents=new WeakMap;class Memory{static{__name(this,"Memory")}transcript;constructor(transcript,content){this.transcript=transcript,contents.set(this,{id:randomUUID(),content:Memory.prepare(transcript,content)}),Object.freeze(this)}get id(){return contents.get(this).id}get topic(){return contents.get(this).content.topic}get content(){return contents.get(this).content.content}get sourceIndexes(){return contents.get(this).content.sourceIndexes}get sourceResults(){return contents.get(this).content.sourceResults}snapshot(){return contents.get(this).content}toJSON(){return{id:this.id,...contents.get(this).content}}static prepare(transcript,content){if(!isObject(content))throw new TypeError("Memory requires topic, content and unique source indexes");const topic=content.topic,text=content.content,evidence=content.sourceIndexes;if(!isNonBlankString(topic)||!isNonBlankString(text)||!isNonEmptyArray(evidence))throw new TypeError("Memory requires topic, content and unique source indexes");const sourceIndexes=Object.freeze([...evidence]);if(!isNonEmptyArray(sourceIndexes)||!hasUniqueItems(sourceIndexes))throw new TypeError("Memory requires topic, content and unique source indexes");for(const index of sourceIndexes)transcript.ref(index);const sourceResults=prepareMemoryResultSources(transcript,content.sourceResults);return Object.freeze({topic,content:text,sourceIndexes,...sourceResults?{sourceResults}:{}})}}function updateMemory(memory,content){contents.get(memory).content=content}__name(updateMemory,"updateMemory");function restoreMemory(transcript,state){if(!isObject(state)||!isNonBlankString(state.id))throw new TypeError("Invalid persisted memory identity");const memory=new Memory(transcript,state);return contents.set(memory,{id:state.id,content:memory.snapshot()}),memory}__name(restoreMemory,"restoreMemory");export{Memory,restoreMemory,updateMemory};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { MemoryContent } from '@weiliang0121/capix-protocol';
|
|
2
|
+
import type { MemoryApplyState } from './apply.js';
|
|
3
|
+
export interface MemoryState extends MemoryContent {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
}
|
|
6
|
+
export interface MemoriesState {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly transcriptId: string;
|
|
9
|
+
readonly sequence: number;
|
|
10
|
+
readonly items: readonly MemoryState[];
|
|
11
|
+
readonly applies?: readonly MemoryApplyState[];
|
|
12
|
+
}
|
|
13
|
+
export interface MemoriesCommit {
|
|
14
|
+
readonly memoriesId: string;
|
|
15
|
+
readonly transcriptId: string;
|
|
16
|
+
readonly expectedSequence: number;
|
|
17
|
+
readonly sequence: number;
|
|
18
|
+
readonly upserts: readonly MemoryState[];
|
|
19
|
+
readonly fixed?: MemoryApplyState;
|
|
20
|
+
}
|
|
21
|
+
export interface MemoriesPersistence {
|
|
22
|
+
commit(change: MemoriesCommit): undefined;
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/*! MIT License | Copyright (c) 2026 wei.liang | See LICENSE */
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ResourceOffer } from '@weiliang0121/capix-resources';
|
|
2
|
+
import type { Memories } from './memories.js';
|
|
3
|
+
import type { MemoryChange } from './change.js';
|
|
4
|
+
import type { MemoryState } from './persistence.js';
|
|
5
|
+
type MemorySnapshot = ReturnType<Memories['snapshot']>;
|
|
6
|
+
export interface CapturedMemories {
|
|
7
|
+
readonly memoriesId: string;
|
|
8
|
+
readonly sequence: number;
|
|
9
|
+
readonly items: Readonly<MemorySnapshot>;
|
|
10
|
+
}
|
|
11
|
+
export interface MemoryBasis {
|
|
12
|
+
readonly memoriesId: string;
|
|
13
|
+
readonly transcriptId: string;
|
|
14
|
+
readonly sequence: number;
|
|
15
|
+
}
|
|
16
|
+
export interface MemoryApplication extends MemoryBasis {
|
|
17
|
+
readonly startSequence: number;
|
|
18
|
+
readonly items: readonly Readonly<MemoryState>[];
|
|
19
|
+
}
|
|
20
|
+
export declare const MemoryResource: Readonly<{
|
|
21
|
+
snapshot: ResourceOffer<Memories | CapturedMemories, readonly {
|
|
22
|
+
topic: string;
|
|
23
|
+
content: string;
|
|
24
|
+
sourceIndexes: readonly number[];
|
|
25
|
+
sourceResults?: readonly import("@weiliang0121/capix-protocol").MemoryResultSource[];
|
|
26
|
+
id: string;
|
|
27
|
+
}[], CapturedMemories, never>;
|
|
28
|
+
apply: ResourceOffer<Memories, Readonly<{
|
|
29
|
+
apply(changes: readonly MemoryChange[], expected?: MemoryBasis): MemoryApplication;
|
|
30
|
+
applyFixed(input: {
|
|
31
|
+
readonly position: string;
|
|
32
|
+
readonly changes: readonly MemoryChange[];
|
|
33
|
+
}, expected?: MemoryBasis): import("./apply.js").MemoryApplyReceipt;
|
|
34
|
+
}>, Memories, never>;
|
|
35
|
+
}>;
|
|
36
|
+
export {};
|
package/dist/resource.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! MIT License | Copyright (c) 2026 wei.liang | See LICENSE */
|
|
2
|
+
var __defProp=Object.defineProperty;var __name=(target,value)=>__defProp(target,"name",{value,configurable:!0});import{ResourceOffer}from"@weiliang0121/capix-resources";import{RuntimeError}from"@weiliang0121/capix-contracts";import{isNonBlankString,isObject}from"./validation.js";function checkBasis(memories,expected,sequence){if(!isObject(expected))throw new TypeError("Invalid memory application basis");if(!isNonBlankString(expected.memoriesId)||!isNonBlankString(expected.transcriptId)||!Number.isSafeInteger(expected.sequence)||expected.sequence<0)throw new TypeError("Invalid memory application basis");if(expected.memoriesId!==memories.id||expected.transcriptId!==memories.transcript.id||expected.sequence!==sequence)throw new RuntimeError("MEMORY_BASIS_CONFLICT","Memory application basis does not match the fixed collection version")}__name(checkBasis,"checkBasis");const MemoryResource=Object.freeze({snapshot:new ResourceOffer({name:"memories.snapshot",expose:__name(captured=>captured.items,"expose"),timing:"binding",capture:__name(memories=>"items"in memories?memories:{memoriesId:memories.id,sequence:memories.sequence,items:Object.freeze(memories.snapshot().map(memory=>Object.freeze(memory)))},"capture"),describe:__name(captured=>({memoriesId:captured.memoriesId,sequence:captured.sequence,memoryIds:captured.items.map(memory=>memory.id)}),"describe")}),apply:new ResourceOffer({name:"memories.apply",expose:__name((memories,activity)=>Object.freeze({apply(changes,expected){return activity.runSync("apply",{input:__name(()=>({count:changes.length}),"input"),output:__name(result=>({memoriesId:result.memoriesId,startSequence:result.startSequence,endSequence:result.sequence,memoryIds:result.items.map(memory=>memory.id)}),"output")},()=>{expected!==void 0&&checkBasis(memories,expected,memories.sequence);const startSequence=memories.sequence,applied=memories.apply(changes,{origin:activity.origin,observation:activity.memoryObservation});return Object.freeze({memoriesId:memories.id,transcriptId:memories.transcript.id,startSequence,sequence:memories.sequence,items:Object.freeze(applied.map(memory=>Object.freeze(memory.toJSON())))})})},applyFixed(input,expected){return activity.runSync("applyFixed",{input:__name(()=>({position:input.position,count:input.changes.length}),"input"),output:__name(receipt=>receipt,"output")},()=>{if(!activity.origin)throw new Error("Fixed memory application requires a calling identity");const request={contract:{key:"memories.apply",version:1},operation:{contextId:activity.origin.contextId,position:input.position},memoriesId:memories.id,changes:input.changes},confirmed=memories.confirmFixed(request);return expected!==void 0&&checkBasis(memories,expected,confirmed?.startSequence??memories.sequence),confirmed??memories.applyFixed(request,{origin:activity.origin,observation:activity.memoryObservation})})}}),"expose"),timing:"access",describe:__name(memories=>({memoriesId:memories.id}),"describe")})});export{MemoryResource};
|
package/dist/source.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { MemoryResultSource } from '@weiliang0121/capix-protocol';
|
|
2
|
+
import type { Transcript } from '@weiliang0121/capix-transcript';
|
|
3
|
+
export declare function prepareMemoryResultSources(transcript: Transcript, sources: readonly MemoryResultSource[] | undefined): readonly MemoryResultSource[] | undefined;
|
package/dist/source.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! MIT License | Copyright (c) 2026 wei.liang | See LICENSE */
|
|
2
|
+
var __defProp=Object.defineProperty;var __name=(target,value)=>__defProp(target,"name",{value,configurable:!0});import{isPlainObject}from"es-toolkit/predicate";import{isArray}from"es-toolkit/compat";import{isNonBlankString}from"./validation.js";function prepareMemoryResultSources(transcript,sources){if(sources===void 0)return;if(!isArray(sources))throw new TypeError("Memory result sources must be an array");const ids=new Set,prepared=Array.from(sources,source=>{if(!isPlainObject(source))throw new TypeError("Memory result source requires a known kind and unique identity");const id=source.id,kind=source.kind,answerIndex=source.answerIndex;if(kind!=="file-created"||!isNonBlankString(id)||ids.has(id))throw new TypeError("Memory result source requires a known kind and unique identity");if(ids.add(id),transcript.ref(answerIndex).read().role!=="assistant")throw new TypeError("Memory result source must locate an assistant answer");const receipt=source.receipt;if(!isPlainObject(receipt))throw new TypeError("Memory result source requires a valid file creation receipt");const path=receipt.path,bytes=receipt.bytes,digest=receipt.digest;if(!isNonBlankString(path)||!Number.isSafeInteger(bytes)||bytes<0||typeof digest!="string"||!/^[a-f0-9]{64}$/u.test(digest))throw new TypeError("Memory result source requires a valid file creation receipt");return Object.freeze({id,kind,answerIndex,receipt:Object.freeze({path,bytes,digest})})});return prepared.length?Object.freeze(prepared):void 0}__name(prepareMemoryResultSources,"prepareMemoryResultSources");export{prepareMemoryResultSources};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function isNonBlankString(value: unknown): value is string;
|
|
2
|
+
export declare function isNonEmptyArray(value: unknown): value is [unknown, ...unknown[]];
|
|
3
|
+
export declare function hasUniqueItems(values: readonly unknown[]): boolean;
|
|
4
|
+
export declare function isObject(value: unknown): value is Record<string, unknown>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! MIT License | Copyright (c) 2026 wei.liang | See LICENSE */
|
|
2
|
+
var __defProp=Object.defineProperty;var __name=(target,value)=>__defProp(target,"name",{value,configurable:!0});import{isString}from"es-toolkit/predicate";import{isArray}from"es-toolkit/compat";function isNonBlankString(value){return isString(value)&&/\S/.test(value)}__name(isNonBlankString,"isNonBlankString");function isNonEmptyArray(value){return isArray(value)&&value.length>0}__name(isNonEmptyArray,"isNonEmptyArray");function hasUniqueItems(values){return new Set(values).size===values.length}__name(hasUniqueItems,"hasUniqueItems");function isObject(value){return typeof value=="object"&&value!==null&&!isArray(value)}__name(isObject,"isObject");export{hasUniqueItems,isNonBlankString,isNonEmptyArray,isObject};
|
package/dist/view.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SyncViewSource } from '@weiliang0121/capix-view';
|
|
2
|
+
import type { Memories } from './memories.js';
|
|
3
|
+
import type { MemoryState } from './persistence.js';
|
|
4
|
+
export interface MemoryViewSummary {
|
|
5
|
+
readonly topic: string;
|
|
6
|
+
}
|
|
7
|
+
type MemoryViewContract = SyncViewSource<string, MemoryViewSummary, MemoryState>;
|
|
8
|
+
type SourceStamp = Parameters<MemoryViewContract['read']>[1];
|
|
9
|
+
export declare class MemoryViewSource implements MemoryViewContract {
|
|
10
|
+
#private;
|
|
11
|
+
readonly id: string;
|
|
12
|
+
readonly scope: string;
|
|
13
|
+
constructor(memories: Memories, keys?: readonly string[]);
|
|
14
|
+
index(): ReturnType<MemoryViewContract['index']>;
|
|
15
|
+
inspect(keys: readonly string[]): ReturnType<MemoryViewContract['inspect']>;
|
|
16
|
+
read(keys: readonly string[], expected: SourceStamp): ReturnType<MemoryViewContract['read']>;
|
|
17
|
+
check(expected: SourceStamp): boolean;
|
|
18
|
+
private stamp;
|
|
19
|
+
private requireCurrent;
|
|
20
|
+
private acceptKeys;
|
|
21
|
+
private requireMemory;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
package/dist/view.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! MIT License | Copyright (c) 2026 wei.liang | See LICENSE */
|
|
2
|
+
var __defProp=Object.defineProperty;var __name=(target,value)=>__defProp(target,"name",{value,configurable:!0});import{RuntimeError}from"@weiliang0121/capix-contracts";class MemoryViewSource{static{__name(this,"MemoryViewSource")}id;scope;#memories;#keys;#allowed;constructor(memories,keys){if(this.#memories=memories,this.id=memories.id,keys!==void 0){this.#keys=this.acceptKeys(keys);for(const key of this.#keys)this.requireMemory(key);this.#allowed=new Set(this.#keys)}this.scope=this.#keys===void 0?"whole":`keys:${JSON.stringify(this.#keys)}`,Object.freeze(this)}index(){const stamp=this.stamp(),keys=this.#keys??this.#memories.values().map(memory=>memory.id),entries=Object.freeze(keys.map(key=>Object.freeze({key,summary:Object.freeze({topic:this.requireMemory(key).topic})})));return this.requireCurrent(stamp),Object.freeze({stamp,entries})}inspect(keys){const stamp=this.stamp(),selected=this.acceptKeys(keys),entries=Object.freeze(selected.map(key=>{if(this.#allowed&&!this.#allowed.has(key))throw new RangeError("Memory is outside the view source scope");return Object.freeze({key,summary:Object.freeze({topic:this.requireMemory(key).topic})})}));return this.requireCurrent(stamp),Object.freeze({stamp,entries})}read(keys,expected){const basis=Object.freeze({id:expected.id,scope:expected.scope,version:expected.version});this.requireCurrent(basis);const selected=this.acceptKeys(keys),entries=Object.freeze(selected.map(key=>{if(this.#allowed&&!this.#allowed.has(key))throw new RangeError("Memory is outside the view source scope");return Object.freeze({key,value:Object.freeze(this.requireMemory(key).toJSON())})}));return this.requireCurrent(basis),Object.freeze({stamp:this.stamp(),entries})}check(expected){return expected?.id===this.id&&expected.scope===this.scope&&expected.version===String(this.#memories.sequence)}stamp(){return Object.freeze({id:this.id,scope:this.scope,version:String(this.#memories.sequence)})}requireCurrent(expected){if(!this.check(expected))throw new RuntimeError("VIEW_SOURCE_CONFLICT","Memory view source has changed")}acceptKeys(keys){if(!Array.isArray(keys))throw new TypeError("Memory view keys must be an array");const accepted=Array.from(keys);if(accepted.some(key=>typeof key!="string"||!key)||new Set(accepted).size!==accepted.length)throw new TypeError("Memory view requires unique nonempty string keys");return Object.freeze(accepted)}requireMemory(key){const memory=this.#memories.get(key);if(!memory)throw new RangeError("Memory view member does not exist");return memory}}export{MemoryViewSource};
|
package/package.json
CHANGED
|
@@ -1,6 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weiliang0121/capix-memory",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"es-toolkit": "1.52.0",
|
|
14
|
+
"@weiliang0121/capix-transcript": "0.1.0",
|
|
15
|
+
"@weiliang0121/capix-changes": "0.1.0",
|
|
16
|
+
"@weiliang0121/capix-resources": "0.1.0",
|
|
17
|
+
"@weiliang0121/capix-view": "0.1.0",
|
|
18
|
+
"@weiliang0121/capix-protocol": "0.1.0",
|
|
19
|
+
"@weiliang0121/capix-contracts": "0.1.0",
|
|
20
|
+
"@weiliang0121/capix-diagnostics": "0.1.0"
|
|
21
|
+
},
|
|
22
|
+
"description": "Versioned memory entities and controlled resource access for CAPiX.",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"author": "wei.liang (https://github.com/weiliang0121)",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/weiliang0121/capix.git",
|
|
28
|
+
"directory": "server/memory"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=22.23.2"
|
|
32
|
+
},
|
|
33
|
+
"main": "./dist/index.js",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"files": [
|
|
36
|
+
"dist/**/*.js",
|
|
37
|
+
"dist/**/*.d.ts",
|
|
38
|
+
"LICENSE",
|
|
39
|
+
"README.md"
|
|
40
|
+
],
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public",
|
|
43
|
+
"registry": "https://registry.npmjs.org"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsc -p tsconfig.json",
|
|
47
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
48
|
+
}
|
|
6
49
|
}
|