@noetic-tools/context 1.0.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 +201 -0
- package/README.md +28 -0
- package/dist/context/budget.d.ts +39 -0
- package/dist/context/budget.js +183 -0
- package/dist/context/exec-context-factory.d.ts +24 -0
- package/dist/context/exec-context-factory.js +30 -0
- package/dist/context/function-call-utils.d.ts +6 -0
- package/dist/context/function-call-utils.js +27 -0
- package/dist/context/layer-api.d.ts +11 -0
- package/dist/context/layer-api.js +91 -0
- package/dist/context/layer-lifecycle.d.ts +239 -0
- package/dist/context/layer-lifecycle.js +800 -0
- package/dist/context/layer-provides.d.ts +32 -0
- package/dist/context/layer-provides.js +33 -0
- package/dist/context/layer-usage.d.ts +13 -0
- package/dist/context/layer-usage.js +54 -0
- package/dist/context/layers/durable-task-state.d.ts +107 -0
- package/dist/context/layers/durable-task-state.js +250 -0
- package/dist/context/layers/file-reference.d.ts +55 -0
- package/dist/context/layers/file-reference.js +729 -0
- package/dist/context/layers/history-window.d.ts +31 -0
- package/dist/context/layers/history-window.js +150 -0
- package/dist/context/layers/observational-context.d.ts +56 -0
- package/dist/context/layers/observational-context.js +152 -0
- package/dist/context/layers/plan-context.d.ts +73 -0
- package/dist/context/layers/plan-context.js +749 -0
- package/dist/context/layers/static-content.d.ts +27 -0
- package/dist/context/layers/static-content.js +46 -0
- package/dist/context/layers/steering.d.ts +50 -0
- package/dist/context/layers/steering.js +291 -0
- package/dist/context/layers/temporal-context.d.ts +91 -0
- package/dist/context/layers/temporal-context.js +327 -0
- package/dist/context/layers/tool-context-layer.d.ts +15 -0
- package/dist/context/layers/tool-context-layer.js +43 -0
- package/dist/context/layers/working-memory-context.d.ts +46 -0
- package/dist/context/layers/working-memory-context.js +155 -0
- package/dist/context/projector.d.ts +19 -0
- package/dist/context/projector.js +89 -0
- package/dist/context/scope.d.ts +3 -0
- package/dist/context/scope.js +42 -0
- package/dist/context/storage-batch.d.ts +18 -0
- package/dist/context/storage-batch.js +35 -0
- package/dist/context/strip-unresolved.d.ts +13 -0
- package/dist/context/strip-unresolved.js +35 -0
- package/dist/deprecated.d.ts +43 -0
- package/dist/deprecated.js +28 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +31 -0
- package/package.json +63 -0
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ContextScope, ExecutionContext, ScopedStorage, StorageAdapter } from '@noetic-tools/types';
|
|
2
|
+
export declare function resolveScopeKey(scope: ContextScope, ctx: ExecutionContext): string;
|
|
3
|
+
export declare function createScopedStorage(storage: StorageAdapter, layerId: string, scopeKey: string): ScopedStorage;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { storageGetMany } from './storage-batch.js';
|
|
2
|
+
export function resolveScopeKey(scope, ctx) {
|
|
3
|
+
switch (scope) {
|
|
4
|
+
case 'thread':
|
|
5
|
+
return ctx.threadId;
|
|
6
|
+
case 'resource':
|
|
7
|
+
return ctx.resourceId ?? ctx.threadId;
|
|
8
|
+
case 'global':
|
|
9
|
+
return '__global__';
|
|
10
|
+
case 'execution':
|
|
11
|
+
return ctx.executionId;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function createScopedStorage(storage, layerId, scopeKey) {
|
|
15
|
+
const prefix = `layers/${layerId}/${scopeKey}/`;
|
|
16
|
+
return {
|
|
17
|
+
async get(key) {
|
|
18
|
+
return storage.get(`${prefix}${key}`);
|
|
19
|
+
},
|
|
20
|
+
async set(key, value) {
|
|
21
|
+
return storage.set(`${prefix}${key}`, value);
|
|
22
|
+
},
|
|
23
|
+
async delete(key) {
|
|
24
|
+
return storage.delete(`${prefix}${key}`);
|
|
25
|
+
},
|
|
26
|
+
async list(keyPrefix) {
|
|
27
|
+
const fullPrefix = keyPrefix ? `${prefix}${keyPrefix}` : prefix;
|
|
28
|
+
const keys = await storage.list(fullPrefix);
|
|
29
|
+
return keys.map((k) => (k.startsWith(prefix) ? k.slice(prefix.length) : k));
|
|
30
|
+
},
|
|
31
|
+
async getMany(keys) {
|
|
32
|
+
const scoped = await storageGetMany(storage, keys.map((k) => `${prefix}${k}`));
|
|
33
|
+
// Hand back the keys the caller passed in, not the namespaced ones —
|
|
34
|
+
// `list` already strips the prefix and a layer never sees it otherwise.
|
|
35
|
+
const unscoped = new Map();
|
|
36
|
+
for (const [key, value] of scoped) {
|
|
37
|
+
unscoped.set(key.startsWith(prefix) ? key.slice(prefix.length) : key, value);
|
|
38
|
+
}
|
|
39
|
+
return unscoped;
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batch reads over a `StorageAdapter`, with the fallback every caller would
|
|
3
|
+
* otherwise write itself.
|
|
4
|
+
*
|
|
5
|
+
* `StorageAdapter.getMany` is optional — an adapter published before it existed
|
|
6
|
+
* is still valid — so no consumer may call it directly. They call this instead:
|
|
7
|
+
* one round trip where the backend supports it, a parallel `get` sweep where it
|
|
8
|
+
* does not. The sweep is deliberately parallel, since the loops this replaces
|
|
9
|
+
* were serial and paid full latency per key.
|
|
10
|
+
*/
|
|
11
|
+
import type { StorageAdapter } from '@noetic-tools/types';
|
|
12
|
+
/**
|
|
13
|
+
* @public
|
|
14
|
+
* Read many keys at once. Keys with no stored value are absent from the result,
|
|
15
|
+
* so both paths agree: `null` never appears as a value, and `map.size` counts
|
|
16
|
+
* only what was actually there.
|
|
17
|
+
*/
|
|
18
|
+
export declare function storageGetMany<T>(storage: Pick<StorageAdapter, 'get' | 'getMany'>, keys: string[]): Promise<Map<string, T>>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batch reads over a `StorageAdapter`, with the fallback every caller would
|
|
3
|
+
* otherwise write itself.
|
|
4
|
+
*
|
|
5
|
+
* `StorageAdapter.getMany` is optional — an adapter published before it existed
|
|
6
|
+
* is still valid — so no consumer may call it directly. They call this instead:
|
|
7
|
+
* one round trip where the backend supports it, a parallel `get` sweep where it
|
|
8
|
+
* does not. The sweep is deliberately parallel, since the loops this replaces
|
|
9
|
+
* were serial and paid full latency per key.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* @public
|
|
13
|
+
* Read many keys at once. Keys with no stored value are absent from the result,
|
|
14
|
+
* so both paths agree: `null` never appears as a value, and `map.size` counts
|
|
15
|
+
* only what was actually there.
|
|
16
|
+
*/
|
|
17
|
+
export async function storageGetMany(storage, keys) {
|
|
18
|
+
if (keys.length === 0) {
|
|
19
|
+
/* Touch storage for nothing and a D1-backed adapter still pays a query. */
|
|
20
|
+
return new Map();
|
|
21
|
+
}
|
|
22
|
+
if (storage.getMany) {
|
|
23
|
+
return storage.getMany(keys);
|
|
24
|
+
}
|
|
25
|
+
const values = await Promise.all(keys.map((key) => storage.get(key)));
|
|
26
|
+
const found = new Map();
|
|
27
|
+
for (let i = 0; i < keys.length; i++) {
|
|
28
|
+
const value = values[i];
|
|
29
|
+
if (value === null || value === undefined) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
found.set(keys[i], value);
|
|
33
|
+
}
|
|
34
|
+
return found;
|
|
35
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drop dangling tool-call state from an item list. A `function_call` without a
|
|
3
|
+
* matching `function_call_output` (or vice-versa) wedges the model on the next
|
|
4
|
+
* round (it typically fabricates an output), so we filter both sides of any
|
|
5
|
+
* incomplete pair. Order-preserving and pure — safe to run after a slice.
|
|
6
|
+
*
|
|
7
|
+
* Mirrors Claude Code's `filterUnresolvedToolUses`. Used by the CLI's
|
|
8
|
+
* session-resume path to drop dangling pairs left by a crash, and by the
|
|
9
|
+
* `historyWindow` context layer to clean orphans at the slice boundary.
|
|
10
|
+
*/
|
|
11
|
+
import type { Item } from '@noetic-tools/types';
|
|
12
|
+
/** @public */
|
|
13
|
+
export declare function stripUnresolvedToolCalls(items: ReadonlyArray<Item>): Item[];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drop dangling tool-call state from an item list. A `function_call` without a
|
|
3
|
+
* matching `function_call_output` (or vice-versa) wedges the model on the next
|
|
4
|
+
* round (it typically fabricates an output), so we filter both sides of any
|
|
5
|
+
* incomplete pair. Order-preserving and pure — safe to run after a slice.
|
|
6
|
+
*
|
|
7
|
+
* Mirrors Claude Code's `filterUnresolvedToolUses`. Used by the CLI's
|
|
8
|
+
* session-resume path to drop dangling pairs left by a crash, and by the
|
|
9
|
+
* `historyWindow` context layer to clean orphans at the slice boundary.
|
|
10
|
+
*/
|
|
11
|
+
/** @public */
|
|
12
|
+
export function stripUnresolvedToolCalls(items) {
|
|
13
|
+
const outputCallIds = new Set();
|
|
14
|
+
const callCallIds = new Set();
|
|
15
|
+
for (const item of items) {
|
|
16
|
+
if (item.type === 'function_call_output') {
|
|
17
|
+
outputCallIds.add(item.callId);
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (item.type === 'function_call') {
|
|
21
|
+
callCallIds.add(item.callId);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const keep = [];
|
|
25
|
+
for (const item of items) {
|
|
26
|
+
if (item.type === 'function_call' && !outputCallIds.has(item.callId)) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (item.type === 'function_call_output' && !callCallIds.has(item.callId)) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
keep.push(item);
|
|
33
|
+
}
|
|
34
|
+
return keep;
|
|
35
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deprecated aliases from the "memory layer" era of the framework.
|
|
3
|
+
*
|
|
4
|
+
* The layer system assembles the model's *context window* — recall, budget
|
|
5
|
+
* allocation, history projection, the item-append pipeline — so it is named
|
|
6
|
+
* after context, not memory. Every name below still resolves to its
|
|
7
|
+
* replacement; nothing here changes behaviour.
|
|
8
|
+
*
|
|
9
|
+
* These are scheduled for removal in the next major of `@noetic-tools/context`.
|
|
10
|
+
*/
|
|
11
|
+
export type { ContextMemory, InferMemory, InferMemoryShape, MemoryCallModelRequest, MemoryConfig, MemoryHooks, MemoryLayer, MemoryScope, ToolMemory, ToolMemoryDeclaration, } from '@noetic-tools/types';
|
|
12
|
+
export {
|
|
13
|
+
/** @deprecated Renamed to `buildContextData`. */
|
|
14
|
+
buildContextData as buildContextMemory, } from './context/layer-api.js';
|
|
15
|
+
export {
|
|
16
|
+
/** @deprecated Renamed to `observationalContext`. */
|
|
17
|
+
observationalContext as observationalMemory, } from './context/layers/observational-context.js';
|
|
18
|
+
export {
|
|
19
|
+
/** @deprecated Renamed to `planContext`. */
|
|
20
|
+
planContext as planMemory, } from './context/layers/plan-context.js';
|
|
21
|
+
export {
|
|
22
|
+
/** @deprecated Renamed to `temporalContext`. */
|
|
23
|
+
temporalContext as temporalMemory, } from './context/layers/temporal-context.js';
|
|
24
|
+
export {
|
|
25
|
+
/** @deprecated Renamed to `toolContextLayer`. */
|
|
26
|
+
toolContextLayer as toolMemoryLayer, } from './context/layers/tool-context-layer.js';
|
|
27
|
+
export {
|
|
28
|
+
/** @deprecated Renamed to `workingMemoryContext`. */
|
|
29
|
+
workingMemoryContext as workingMemory, } from './context/layers/working-memory-context.js';
|
|
30
|
+
import type { ObservationalContextConfig } from './context/layers/observational-context.js';
|
|
31
|
+
import type { PlanContextConfig } from './context/layers/plan-context.js';
|
|
32
|
+
import type { TemporalContextConfig } from './context/layers/temporal-context.js';
|
|
33
|
+
import type { WorkingMemoryContextConfig, WorkingMemoryContextState } from './context/layers/working-memory-context.js';
|
|
34
|
+
/** @deprecated Renamed to {@link WorkingMemoryContextConfig}. */
|
|
35
|
+
export type WorkingMemoryConfig = WorkingMemoryContextConfig;
|
|
36
|
+
/** @deprecated Renamed to {@link WorkingMemoryContextState}. */
|
|
37
|
+
export type WorkingMemoryState = WorkingMemoryContextState;
|
|
38
|
+
/** @deprecated Renamed to {@link ObservationalContextConfig}. */
|
|
39
|
+
export type ObservationalMemoryConfig = ObservationalContextConfig;
|
|
40
|
+
/** @deprecated Renamed to {@link TemporalContextConfig}. */
|
|
41
|
+
export type TemporalMemoryConfig = TemporalContextConfig;
|
|
42
|
+
/** @deprecated Renamed to {@link PlanContextConfig}. */
|
|
43
|
+
export type PlanMemoryConfig = PlanContextConfig;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deprecated aliases from the "memory layer" era of the framework.
|
|
3
|
+
*
|
|
4
|
+
* The layer system assembles the model's *context window* — recall, budget
|
|
5
|
+
* allocation, history projection, the item-append pipeline — so it is named
|
|
6
|
+
* after context, not memory. Every name below still resolves to its
|
|
7
|
+
* replacement; nothing here changes behaviour.
|
|
8
|
+
*
|
|
9
|
+
* These are scheduled for removal in the next major of `@noetic-tools/context`.
|
|
10
|
+
*/
|
|
11
|
+
export {
|
|
12
|
+
/** @deprecated Renamed to `buildContextData`. */
|
|
13
|
+
buildContextData as buildContextMemory, } from './context/layer-api.js';
|
|
14
|
+
export {
|
|
15
|
+
/** @deprecated Renamed to `observationalContext`. */
|
|
16
|
+
observationalContext as observationalMemory, } from './context/layers/observational-context.js';
|
|
17
|
+
export {
|
|
18
|
+
/** @deprecated Renamed to `planContext`. */
|
|
19
|
+
planContext as planMemory, } from './context/layers/plan-context.js';
|
|
20
|
+
export {
|
|
21
|
+
/** @deprecated Renamed to `temporalContext`. */
|
|
22
|
+
temporalContext as temporalMemory, } from './context/layers/temporal-context.js';
|
|
23
|
+
export {
|
|
24
|
+
/** @deprecated Renamed to `toolContextLayer`. */
|
|
25
|
+
toolContextLayer as toolMemoryLayer, } from './context/layers/tool-context-layer.js';
|
|
26
|
+
export {
|
|
27
|
+
/** @deprecated Renamed to `workingMemoryContext`. */
|
|
28
|
+
workingMemoryContext as workingMemory, } from './context/layers/working-memory-context.js';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @noetic-tools/context — the context layer system for Noetic agents: the
|
|
3
|
+
* ContextLayer contract, lifecycle/budget/projection machinery, and built-in
|
|
4
|
+
* layer implementations.
|
|
5
|
+
*/
|
|
6
|
+
export type { ContextScope, ExecutionContext, ExecutionOutcome, LayerCallModelRequest, } from '@noetic-tools/types';
|
|
7
|
+
export * from '@noetic-tools/types/contract';
|
|
8
|
+
export * from './context/budget.js';
|
|
9
|
+
export * from './context/exec-context-factory.js';
|
|
10
|
+
export * from './context/function-call-utils.js';
|
|
11
|
+
export * from './context/layer-api.js';
|
|
12
|
+
export * from './context/layer-lifecycle.js';
|
|
13
|
+
export * from './context/layer-provides.js';
|
|
14
|
+
export * from './context/layer-usage.js';
|
|
15
|
+
export * from './context/layers/durable-task-state.js';
|
|
16
|
+
export * from './context/layers/file-reference.js';
|
|
17
|
+
export * from './context/layers/history-window.js';
|
|
18
|
+
export * from './context/layers/observational-context.js';
|
|
19
|
+
export * from './context/layers/plan-context.js';
|
|
20
|
+
export * from './context/layers/static-content.js';
|
|
21
|
+
export * from './context/layers/steering.js';
|
|
22
|
+
export * from './context/layers/temporal-context.js';
|
|
23
|
+
export * from './context/layers/tool-context-layer.js';
|
|
24
|
+
export * from './context/layers/working-memory-context.js';
|
|
25
|
+
export * from './context/projector.js';
|
|
26
|
+
export * from './context/scope.js';
|
|
27
|
+
export * from './context/storage-batch.js';
|
|
28
|
+
export * from './context/strip-unresolved.js';
|
|
29
|
+
export * from './deprecated.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @noetic-tools/context — the context layer system for Noetic agents: the
|
|
3
|
+
* ContextLayer contract, lifecycle/budget/projection machinery, and built-in
|
|
4
|
+
* layer implementations.
|
|
5
|
+
*/
|
|
6
|
+
// The ContextLayer contract is foundational (referenced by Context/Step/runtime
|
|
7
|
+
// types), so it physically lives in @noetic-tools/types. Re-export it here so
|
|
8
|
+
// @noetic-tools/context remains the one-stop import for context-layer authoring.
|
|
9
|
+
export * from '@noetic-tools/types/contract';
|
|
10
|
+
export * from './context/budget.js';
|
|
11
|
+
export * from './context/exec-context-factory.js';
|
|
12
|
+
export * from './context/function-call-utils.js';
|
|
13
|
+
export * from './context/layer-api.js';
|
|
14
|
+
export * from './context/layer-lifecycle.js';
|
|
15
|
+
export * from './context/layer-provides.js';
|
|
16
|
+
export * from './context/layer-usage.js';
|
|
17
|
+
export * from './context/layers/durable-task-state.js';
|
|
18
|
+
export * from './context/layers/file-reference.js';
|
|
19
|
+
export * from './context/layers/history-window.js';
|
|
20
|
+
export * from './context/layers/observational-context.js';
|
|
21
|
+
export * from './context/layers/plan-context.js';
|
|
22
|
+
export * from './context/layers/static-content.js';
|
|
23
|
+
export * from './context/layers/steering.js';
|
|
24
|
+
export * from './context/layers/temporal-context.js';
|
|
25
|
+
export * from './context/layers/tool-context-layer.js';
|
|
26
|
+
export * from './context/layers/working-memory-context.js';
|
|
27
|
+
export * from './context/projector.js';
|
|
28
|
+
export * from './context/scope.js';
|
|
29
|
+
export * from './context/storage-batch.js';
|
|
30
|
+
export * from './context/strip-unresolved.js';
|
|
31
|
+
export * from './deprecated.js';
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@noetic-tools/context",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Context layer system for Noetic agents: the ContextLayer contract, lifecycle, budget/projection machinery, and built-in layer implementations.",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/mattapperson/noetic.git",
|
|
10
|
+
"directory": "packages/context"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/mattapperson/noetic#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/mattapperson/noetic/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"noetic",
|
|
18
|
+
"agents",
|
|
19
|
+
"context",
|
|
20
|
+
"llm",
|
|
21
|
+
"memory"
|
|
22
|
+
],
|
|
23
|
+
"main": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"default": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"bun-types": "latest"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"zod": "^4.0.0"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@noetic-tools/types": "^2.3.0",
|
|
48
|
+
"zod": "^4.0.0"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "bunx tsc -p tsconfig.build.json && bun scripts/add-js-extensions.ts",
|
|
52
|
+
"verify:esm": "bun scripts/verify-node-esm.ts",
|
|
53
|
+
"prepublishOnly": "rm -rf dist && bun run build && bun run verify:esm && bun scripts/pin-workspace-deps.ts && bun scripts/strip-dev-conditions.ts",
|
|
54
|
+
"test": "bun test",
|
|
55
|
+
"test:coverage": "bun test --coverage --coverage-reporter=text --coverage-reporter=lcov",
|
|
56
|
+
"test:ci": "bun test --coverage --coverage-reporter=lcov",
|
|
57
|
+
"typecheck": "bunx tsc --noEmit",
|
|
58
|
+
"lint": "bunx biome check .",
|
|
59
|
+
"lint:fix": "bunx biome check --fix .",
|
|
60
|
+
"format": "bunx biome format --write .",
|
|
61
|
+
"format:check": "bunx biome format ."
|
|
62
|
+
}
|
|
63
|
+
}
|