@noetic-tools/types 0.1.1 → 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/README.md +4 -0
- package/dist/errors/noetic-error.d.ts +9 -1
- package/dist/errors/noetic-error.js +24 -2
- package/dist/schemas/item.d.ts +20 -1
- package/dist/schemas/item.js +35 -19
- package/dist/types/channel.d.ts +6 -0
- package/dist/types/context.d.ts +20 -3
- package/dist/types/error.d.ts +12 -0
- package/dist/types/memory.d.ts +20 -0
- package/dist/types/runtime.d.ts +16 -2
- package/dist/types/step.d.ts +3 -1
- package/dist/util/message-helpers.d.ts +21 -1
- package/dist/util/message-helpers.js +41 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -14,6 +14,10 @@ both of which re-export the parts relevant to their public surface. Application
|
|
|
14
14
|
code normally imports these types from `@noetic-tools/core` rather than from
|
|
15
15
|
this package directly.
|
|
16
16
|
|
|
17
|
+
The `MemoryLayer` contract is additionally exported at the
|
|
18
|
+
`@noetic-tools/types/contract` subpath for memory-layer authors who want the
|
|
19
|
+
contract without the rest of the vocabulary.
|
|
20
|
+
|
|
17
21
|
## License
|
|
18
22
|
|
|
19
23
|
Apache-2.0
|
|
@@ -11,8 +11,16 @@ export declare class NoeticErrorImpl extends Error {
|
|
|
11
11
|
/**
|
|
12
12
|
* Type guard for `NoeticErrorImpl`.
|
|
13
13
|
*
|
|
14
|
+
* Uses an `instanceof` fast path, then falls back to a structural check
|
|
15
|
+
* (`Error` carrying a `noeticError.kind: string`). The fallback keeps the
|
|
16
|
+
* guard reliable when multiple copies of `@noetic-tools/types` are loaded
|
|
17
|
+
* (e.g. mixed `src`/`dist` resolution or duplicated node_modules), where
|
|
18
|
+
* `instanceof` fails across realms. The inner `kind` is deliberately not
|
|
19
|
+
* validated against the closed `NoeticError` union so errors minted by a
|
|
20
|
+
* newer copy of the package still pass (forward compatibility).
|
|
21
|
+
*
|
|
14
22
|
* @public
|
|
15
23
|
* @param e - Value to check.
|
|
16
|
-
* @returns `true` if `e` is a `NoeticErrorImpl
|
|
24
|
+
* @returns `true` if `e` is a `NoeticErrorImpl` (or structurally equivalent).
|
|
17
25
|
*/
|
|
18
26
|
export declare function isNoeticError(e: unknown): e is NoeticErrorImpl;
|
|
@@ -14,12 +14,30 @@ export class NoeticErrorImpl extends Error {
|
|
|
14
14
|
/**
|
|
15
15
|
* Type guard for `NoeticErrorImpl`.
|
|
16
16
|
*
|
|
17
|
+
* Uses an `instanceof` fast path, then falls back to a structural check
|
|
18
|
+
* (`Error` carrying a `noeticError.kind: string`). The fallback keeps the
|
|
19
|
+
* guard reliable when multiple copies of `@noetic-tools/types` are loaded
|
|
20
|
+
* (e.g. mixed `src`/`dist` resolution or duplicated node_modules), where
|
|
21
|
+
* `instanceof` fails across realms. The inner `kind` is deliberately not
|
|
22
|
+
* validated against the closed `NoeticError` union so errors minted by a
|
|
23
|
+
* newer copy of the package still pass (forward compatibility).
|
|
24
|
+
*
|
|
17
25
|
* @public
|
|
18
26
|
* @param e - Value to check.
|
|
19
|
-
* @returns `true` if `e` is a `NoeticErrorImpl
|
|
27
|
+
* @returns `true` if `e` is a `NoeticErrorImpl` (or structurally equivalent).
|
|
20
28
|
*/
|
|
21
29
|
export function isNoeticError(e) {
|
|
22
|
-
|
|
30
|
+
if (e instanceof NoeticErrorImpl) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
if (!(e instanceof Error)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (!('noeticError' in e)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const inner = e.noeticError;
|
|
40
|
+
return (typeof inner === 'object' && inner !== null && 'kind' in inner && typeof inner.kind === 'string');
|
|
23
41
|
}
|
|
24
42
|
function formatMessage(error) {
|
|
25
43
|
switch (error.kind) {
|
|
@@ -43,6 +61,10 @@ function formatMessage(error) {
|
|
|
43
61
|
return `Budget exceeded: ${error.field} limit ${error.limit}, actual ${error.actual}`;
|
|
44
62
|
case 'steering_denied':
|
|
45
63
|
return `Steering denied${error.guidance ? `: ${error.guidance}` : ''}`;
|
|
64
|
+
case 'item_schema_mismatch':
|
|
65
|
+
return error.itemType
|
|
66
|
+
? `Item type '${error.itemType}' did not match any registered item extension schema.`
|
|
67
|
+
: `Item did not match any registered ${error.category} extension schema.`;
|
|
46
68
|
case 'handle_evicted':
|
|
47
69
|
return `Subprocess handle '${error.handleId}' evicted (step '${error.stepId}'): adapter returned null for ${error.gracePeriodMs}ms`;
|
|
48
70
|
default: {
|
package/dist/schemas/item.d.ts
CHANGED
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
* Use this at trust boundaries (loading persisted session files, accepting
|
|
12
12
|
* externally-supplied transcripts) where we need to reject garbage JSON but
|
|
13
13
|
* don't need to re-validate every provider field TypeScript already types.
|
|
14
|
+
*
|
|
15
|
+
* Extension schemas registered on the registry are **gates, not
|
|
16
|
+
* normalizers**: they validate item shape and the ORIGINAL value is returned
|
|
17
|
+
* on match. Zod transforms, defaults, and key-stripping in extension schemas
|
|
18
|
+
* are unsupported — their output is discarded. Declare pure shape validators.
|
|
14
19
|
*/
|
|
15
20
|
import { z } from 'zod';
|
|
16
21
|
import type { Item, ItemSchemaExtensions } from '../types/items.js';
|
|
@@ -18,7 +23,14 @@ import type { Item, ItemSchemaExtensions } from '../types/items.js';
|
|
|
18
23
|
export declare const ItemSchema: z.ZodCustom<Item, Item>;
|
|
19
24
|
type ItemSchemaCategory = keyof Required<ItemSchemaExtensions>;
|
|
20
25
|
export declare function mergeExtensions(a: ItemSchemaExtensions | undefined, b: ItemSchemaExtensions | undefined): ItemSchemaExtensions;
|
|
21
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* @public Runtime item validator with optional tool, memory-layer, and harness extension schemas.
|
|
28
|
+
*
|
|
29
|
+
* Extension schemas are pure shape validators (gate, not normalizer): on
|
|
30
|
+
* match the original value is returned unchanged, including fields the schema
|
|
31
|
+
* does not declare. Transforms and defaults in extension schemas are
|
|
32
|
+
* unsupported — their parsed output is never used.
|
|
33
|
+
*/
|
|
22
34
|
export declare class ItemSchemaRegistry {
|
|
23
35
|
readonly extensions: ItemSchemaExtensions;
|
|
24
36
|
readonly strictUnknownExtensions: boolean;
|
|
@@ -28,6 +40,13 @@ export declare class ItemSchemaRegistry {
|
|
|
28
40
|
extend(extensions?: ItemSchemaExtensions): ItemSchemaRegistry;
|
|
29
41
|
parse(value: unknown): Item;
|
|
30
42
|
parseMany(values: Iterable<unknown>): Item[];
|
|
43
|
+
/**
|
|
44
|
+
* Validate `value` against the schemas registered for `category` only.
|
|
45
|
+
*
|
|
46
|
+
* When no schemas are registered for the category, falls back to
|
|
47
|
+
* `parse()`. Throws a `NoeticErrorImpl` with kind `item_schema_mismatch`
|
|
48
|
+
* when schemas exist for the category and none match.
|
|
49
|
+
*/
|
|
31
50
|
parseWithCategory(value: unknown, category: ItemSchemaCategory): Item;
|
|
32
51
|
}
|
|
33
52
|
/** @public Default item schema registry. */
|
package/dist/schemas/item.js
CHANGED
|
@@ -11,8 +11,14 @@
|
|
|
11
11
|
* Use this at trust boundaries (loading persisted session files, accepting
|
|
12
12
|
* externally-supplied transcripts) where we need to reject garbage JSON but
|
|
13
13
|
* don't need to re-validate every provider field TypeScript already types.
|
|
14
|
+
*
|
|
15
|
+
* Extension schemas registered on the registry are **gates, not
|
|
16
|
+
* normalizers**: they validate item shape and the ORIGINAL value is returned
|
|
17
|
+
* on match. Zod transforms, defaults, and key-stripping in extension schemas
|
|
18
|
+
* are unsupported — their output is discarded. Declare pure shape validators.
|
|
14
19
|
*/
|
|
15
20
|
import { z } from 'zod';
|
|
21
|
+
import { NoeticErrorImpl } from '../errors/noetic-error.js';
|
|
16
22
|
function isItemLike(value) {
|
|
17
23
|
if (typeof value !== 'object' || value === null) {
|
|
18
24
|
return false;
|
|
@@ -85,15 +91,19 @@ function isKnownBaseType(type) {
|
|
|
85
91
|
type.startsWith('openrouter:'));
|
|
86
92
|
}
|
|
87
93
|
function schemaMismatch(category) {
|
|
88
|
-
return new
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
path: [],
|
|
93
|
-
},
|
|
94
|
-
]);
|
|
94
|
+
return new NoeticErrorImpl({
|
|
95
|
+
kind: 'item_schema_mismatch',
|
|
96
|
+
category,
|
|
97
|
+
});
|
|
95
98
|
}
|
|
96
|
-
/**
|
|
99
|
+
/**
|
|
100
|
+
* @public Runtime item validator with optional tool, memory-layer, and harness extension schemas.
|
|
101
|
+
*
|
|
102
|
+
* Extension schemas are pure shape validators (gate, not normalizer): on
|
|
103
|
+
* match the original value is returned unchanged, including fields the schema
|
|
104
|
+
* does not declare. Transforms and defaults in extension schemas are
|
|
105
|
+
* unsupported — their parsed output is never used.
|
|
106
|
+
*/
|
|
97
107
|
export class ItemSchemaRegistry {
|
|
98
108
|
extensions;
|
|
99
109
|
strictUnknownExtensions;
|
|
@@ -116,25 +126,30 @@ export class ItemSchemaRegistry {
|
|
|
116
126
|
for (const schema of schemas) {
|
|
117
127
|
const parsed = schema.safeParse(value);
|
|
118
128
|
if (parsed.success) {
|
|
119
|
-
return
|
|
129
|
+
// Gate, not normalizer: return the original value, not the
|
|
130
|
+
// zod-stripped clone, so undeclared fields survive validation.
|
|
131
|
+
return base;
|
|
120
132
|
}
|
|
121
133
|
}
|
|
122
134
|
if (this.strictUnknownExtensions && !isKnownBaseType(base.type)) {
|
|
123
|
-
throw new
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
'type',
|
|
129
|
-
],
|
|
130
|
-
},
|
|
131
|
-
]);
|
|
135
|
+
throw new NoeticErrorImpl({
|
|
136
|
+
kind: 'item_schema_mismatch',
|
|
137
|
+
category: 'items',
|
|
138
|
+
itemType: base.type,
|
|
139
|
+
});
|
|
132
140
|
}
|
|
133
141
|
return base;
|
|
134
142
|
}
|
|
135
143
|
parseMany(values) {
|
|
136
144
|
return Array.from(values, (value) => this.parse(value));
|
|
137
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Validate `value` against the schemas registered for `category` only.
|
|
148
|
+
*
|
|
149
|
+
* When no schemas are registered for the category, falls back to
|
|
150
|
+
* `parse()`. Throws a `NoeticErrorImpl` with kind `item_schema_mismatch`
|
|
151
|
+
* when schemas exist for the category and none match.
|
|
152
|
+
*/
|
|
138
153
|
parseWithCategory(value, category) {
|
|
139
154
|
const schemas = schemasForCategory(this.extensions, category);
|
|
140
155
|
if (schemas.length === 0) {
|
|
@@ -143,7 +158,8 @@ export class ItemSchemaRegistry {
|
|
|
143
158
|
for (const schema of schemas) {
|
|
144
159
|
const parsed = schema.safeParse(value);
|
|
145
160
|
if (parsed.success) {
|
|
146
|
-
return
|
|
161
|
+
// Gate, not normalizer: validate shape, return the original value.
|
|
162
|
+
return ItemSchema.parse(value);
|
|
147
163
|
}
|
|
148
164
|
}
|
|
149
165
|
throw schemaMismatch(category);
|
package/dist/types/channel.d.ts
CHANGED
|
@@ -23,6 +23,12 @@ export interface ExternalChannel<T> extends Channel<T> {
|
|
|
23
23
|
}
|
|
24
24
|
/** @public Handle for sending messages to a channel and checking its closed state. */
|
|
25
25
|
export interface ChannelHandle<T> {
|
|
26
|
+
/**
|
|
27
|
+
* External-sender write: synchronous and never back-pressured. When a
|
|
28
|
+
* queue channel is at capacity the OLDEST item is dropped (with a warning)
|
|
29
|
+
* so external callers (HTTP handlers, CLI prompts) never block. Internal
|
|
30
|
+
* senders (`ctx.send`) get async back-pressure instead.
|
|
31
|
+
*/
|
|
26
32
|
send(value: T): void;
|
|
27
33
|
readonly closed: boolean;
|
|
28
34
|
readonly channel: Channel<T>;
|
package/dist/types/context.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { DetachedHandle } from './detached.js';
|
|
|
8
8
|
import type { FsAdapter } from './fs-adapter.js';
|
|
9
9
|
import type { HarnessResponse, StreamEvent, StreamingItem } from './harness-result.js';
|
|
10
10
|
import type { ExecuteInput } from './items.js';
|
|
11
|
-
import type { ContextMemory, MemoryLayer, StorageAdapter } from './memory.js';
|
|
11
|
+
import type { ContextMemory, MemoryLayer, ProjectionPolicy, StorageAdapter } from './memory.js';
|
|
12
12
|
import type { Span } from './observability.js';
|
|
13
13
|
import type { ShellAdapter } from './shell-adapter.js';
|
|
14
14
|
import type { SteeringDecision } from './steering.js';
|
|
@@ -98,6 +98,8 @@ export interface ContextHarness {
|
|
|
98
98
|
readonly config: {
|
|
99
99
|
readonly name: string;
|
|
100
100
|
readonly params: Record<string, unknown>;
|
|
101
|
+
/** Harness-wide default projection policy; a step's `projection` overrides it. */
|
|
102
|
+
readonly projection?: ProjectionPolicy;
|
|
101
103
|
};
|
|
102
104
|
readonly fs: FsAdapter;
|
|
103
105
|
readonly shell: ShellAdapter;
|
|
@@ -128,10 +130,18 @@ export interface ContextHarness {
|
|
|
128
130
|
afterModelCall(layers: MemoryLayer[], response: LLMResponse, ctx: Context): Promise<SteeringDecision>;
|
|
129
131
|
runAppendPipeline(layers: MemoryLayer[], items: import('./items.js').Item[], ctx: Context): Promise<ContextAppendPipelineResult>;
|
|
130
132
|
recallLayers(layers: MemoryLayer[], input: string, ctx: Context): Promise<ContextRecallLayerOutput[]>;
|
|
133
|
+
recallLayersAtomic(layers: MemoryLayer[], input: string, ctx: Context, budgets: Map<string, number>): Promise<ContextRecallLayerOutput[]>;
|
|
134
|
+
recallLayersEventual(layers: MemoryLayer[], input: string, ctx: Context, budgets: Map<string, number>): Promise<ContextRecallLayerOutput[]>;
|
|
131
135
|
projectHistory(layers: MemoryLayer[], items: ReadonlyArray<import('./items.js').Item>, ctx: Context): Promise<ReadonlyArray<import('./items.js').Item>>;
|
|
132
136
|
storeLayers(layers: MemoryLayer[], response: LLMResponse, ctx: Context): Promise<void>;
|
|
133
137
|
previewRequestItems(scope?: unknown): Promise<ReadonlyArray<import('./items.js').Item>>;
|
|
134
|
-
|
|
138
|
+
/**
|
|
139
|
+
* Internal-sender channel write. Resolves immediately unless the target
|
|
140
|
+
* queue channel is at capacity, in which case the send parks until a
|
|
141
|
+
* consumer frees a slot (back-pressure; default 30s timeout →
|
|
142
|
+
* `channel_timeout`, abort → `cancelled`).
|
|
143
|
+
*/
|
|
144
|
+
send<T>(channel: Channel<T>, value: T, ctx: Context): Promise<void>;
|
|
135
145
|
recv<T>(channel: Channel<T>, ctx: Context, opts?: {
|
|
136
146
|
timeout?: number;
|
|
137
147
|
}): Promise<T>;
|
|
@@ -189,7 +199,14 @@ export interface Context<TMemory = ContextMemory, TState = unknown> {
|
|
|
189
199
|
recv<T>(channel: Channel<T>, opts?: {
|
|
190
200
|
timeout?: number;
|
|
191
201
|
}): Promise<T>;
|
|
192
|
-
|
|
202
|
+
/**
|
|
203
|
+
* Send a value into a channel. Resolves immediately for value/topic
|
|
204
|
+
* channels and for queue channels below capacity. When a queue channel is
|
|
205
|
+
* at capacity the returned promise parks until a consumer dequeues an item
|
|
206
|
+
* (back-pressure): after the default 30s timeout it rejects with
|
|
207
|
+
* `channel_timeout`, and aborting the context rejects it with `cancelled`.
|
|
208
|
+
*/
|
|
209
|
+
send<T>(channel: Channel<T>, value: T): Promise<void>;
|
|
193
210
|
tryRecv<T>(channel: Channel<T>): T | null;
|
|
194
211
|
checkpoint(): Promise<void>;
|
|
195
212
|
complete<T>(value: T): void;
|
package/dist/types/error.d.ts
CHANGED
|
@@ -40,6 +40,18 @@ export type NoeticError = {
|
|
|
40
40
|
} | {
|
|
41
41
|
kind: 'cancelled';
|
|
42
42
|
reason?: string;
|
|
43
|
+
} | {
|
|
44
|
+
/**
|
|
45
|
+
* Raised by `ItemSchemaRegistry` when an item fails extension-schema
|
|
46
|
+
* validation: either a category item (e.g. a tool result) matches none
|
|
47
|
+
* of the schemas registered for its category, or — under
|
|
48
|
+
* `strictUnknownExtensions` — an unknown item type matches no
|
|
49
|
+
* registered extension schema. `itemType` is set for the
|
|
50
|
+
* unknown-item-type case.
|
|
51
|
+
*/
|
|
52
|
+
kind: 'item_schema_mismatch';
|
|
53
|
+
category: 'items' | 'developerMessages' | 'toolCalls' | 'toolResults';
|
|
54
|
+
itemType?: string;
|
|
43
55
|
} | {
|
|
44
56
|
kind: 'budget_exceeded';
|
|
45
57
|
field: 'cost' | 'steps' | 'duration';
|
package/dist/types/memory.d.ts
CHANGED
|
@@ -288,6 +288,26 @@ export interface MemoryLayer<TState = unknown> {
|
|
|
288
288
|
hooks: MemoryHooks<TState>;
|
|
289
289
|
/** Per-hook timeout overrides in ms. */
|
|
290
290
|
timeouts?: Partial<LayerTimeouts>;
|
|
291
|
+
/**
|
|
292
|
+
* What to do when this layer's `init` hook throws.
|
|
293
|
+
* - `'throw'` (default): surface the error and abort the execution — memory is
|
|
294
|
+
* load-bearing, and silent disabling hides failures (and, for the steering
|
|
295
|
+
* layer, would fail *open*).
|
|
296
|
+
* - `'disable'`: log a diagnostic and run the execution without this layer
|
|
297
|
+
* (its `recall`/`store`/etc. are skipped). Opt in only for non-critical layers.
|
|
298
|
+
*/
|
|
299
|
+
onInitError?: 'throw' | 'disable';
|
|
300
|
+
/**
|
|
301
|
+
* Recall mode controlling whether this layer's `recall()` blocks the model call.
|
|
302
|
+
* - `'atomic'` (default): recall runs synchronously in the hot path; the
|
|
303
|
+
* harness waits for it before assembling the view.
|
|
304
|
+
* - `'eventual'`: recall is served from cache and never blocks; the cache
|
|
305
|
+
* refreshes after `store()` produces new state, so the next turn sees it.
|
|
306
|
+
*
|
|
307
|
+
* A harness configured with `forceAtomicRecall` treats every layer as atomic,
|
|
308
|
+
* ignoring this field.
|
|
309
|
+
*/
|
|
310
|
+
recallMode?: 'atomic' | 'eventual';
|
|
291
311
|
/** Typed functions and data exposed to code steps via `ctx.memory['layerId']` and automatically as LLM tools. */
|
|
292
312
|
provides?: LayerProvides;
|
|
293
313
|
/** Optional item schemas contributed by this layer, primarily for developer-role memory items. */
|
package/dist/types/runtime.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { DetachedHandle } from './detached.js';
|
|
|
6
6
|
import type { FsAdapter } from './fs-adapter.js';
|
|
7
7
|
import type { HarnessResponse, StreamEvent, StreamingItem } from './harness-result.js';
|
|
8
8
|
import type { ExecuteInput, Item, ItemSchemaExtensions } from './items.js';
|
|
9
|
-
import type { ContextMemory, MemoryLayer, StorageAdapter } from './memory.js';
|
|
9
|
+
import type { ContextMemory, MemoryLayer, ProjectionPolicy, StorageAdapter } from './memory.js';
|
|
10
10
|
import type { Span } from './observability.js';
|
|
11
11
|
import type { ShellAdapter } from './shell-adapter.js';
|
|
12
12
|
import type { SteeringDecision } from './steering.js';
|
|
@@ -28,6 +28,10 @@ export interface AgentConfig<TParams extends Record<string, unknown> = Record<st
|
|
|
28
28
|
itemSchemas?: ItemSchemaExtensions;
|
|
29
29
|
/** Whether unknown extension item types must match a registered schema. Defaults to true. */
|
|
30
30
|
strictItemSchemas?: boolean;
|
|
31
|
+
/** Default projection policy for all LLM steps. Individual steps override via `step.projection`. */
|
|
32
|
+
projection?: ProjectionPolicy;
|
|
33
|
+
/** When true, every layer is recalled atomically regardless of its `recallMode` (no eventual/cached recall). */
|
|
34
|
+
forceAtomicRecall?: boolean;
|
|
31
35
|
}
|
|
32
36
|
/** @public How a message submitted during an active turn is delivered to the model.
|
|
33
37
|
*
|
|
@@ -191,7 +195,13 @@ export interface AgentHarnessContract<TParams extends Record<string, unknown> =
|
|
|
191
195
|
readonly rootCwdState: CwdState;
|
|
192
196
|
/** Update the harness root cwd. Used by the TUI when the user issues a `! cd`. */
|
|
193
197
|
setRootCwd(nextCwd: string): void;
|
|
194
|
-
|
|
198
|
+
/**
|
|
199
|
+
* Internal-sender channel write (back-pressured): resolves immediately
|
|
200
|
+
* unless the queue channel is at capacity, in which case it parks until a
|
|
201
|
+
* consumer frees a slot (30s default timeout → `channel_timeout`; context
|
|
202
|
+
* abort → `cancelled`).
|
|
203
|
+
*/
|
|
204
|
+
send<T>(channel: Channel<T>, value: T, ctx: Context): Promise<void>;
|
|
195
205
|
recv<T>(channel: Channel<T>, ctx: Context, opts?: {
|
|
196
206
|
timeout?: number;
|
|
197
207
|
}): Promise<T>;
|
|
@@ -199,6 +209,10 @@ export interface AgentHarnessContract<TParams extends Record<string, unknown> =
|
|
|
199
209
|
getChannelHandle<T>(channel: ExternalChannel<T>, executionId: string): ChannelHandle<T>;
|
|
200
210
|
initLayers(layers: MemoryLayer[], ctx: Context, storage: StorageAdapter): Promise<void>;
|
|
201
211
|
recallLayers(layers: MemoryLayer[], input: string, ctx: Context): Promise<RecallLayerOutput[]>;
|
|
212
|
+
/** Recall only atomic layers (or all layers when the harness forces atomic recall). Blocks until complete. */
|
|
213
|
+
recallLayersAtomic(layers: MemoryLayer[], input: string, ctx: Context, budgets: Map<string, number>): Promise<RecallLayerOutput[]>;
|
|
214
|
+
/** Recall eventual layers from cache (non-blocking); returns nothing when the harness forces atomic recall. */
|
|
215
|
+
recallLayersEventual(layers: MemoryLayer[], input: string, ctx: Context, budgets: Map<string, number>): Promise<RecallLayerOutput[]>;
|
|
202
216
|
/**
|
|
203
217
|
* Compute the items that would be sent to the model on the next turn for a
|
|
204
218
|
* session — its accumulated history plus the harness's memory-layer recall
|
package/dist/types/step.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { Channel } from './channel.js';
|
|
|
3
3
|
import type { ModelParams, RetryPolicy, StepMeta } from './common.js';
|
|
4
4
|
import type { Context } from './context.js';
|
|
5
5
|
import type { NoeticError } from './error.js';
|
|
6
|
-
import type { ContextMemory, MemoryConfig, MemoryLayer } from './memory.js';
|
|
6
|
+
import type { ContextMemory, MemoryConfig, MemoryLayer, ProjectionPolicy } from './memory.js';
|
|
7
7
|
import type { SubprocessAdapter } from './subprocess-adapter.js';
|
|
8
8
|
import type { Tool } from './tool.js';
|
|
9
9
|
/**
|
|
@@ -99,6 +99,8 @@ export interface StepLLM<TMemory = ContextMemory, _I = unknown, O = unknown> {
|
|
|
99
99
|
params?: ModelParams;
|
|
100
100
|
/** Controls framework event emission for this step. Defaults to `true`. Set `false` to suppress all framework events. A filter function receives `(eventType, data)` and returns `boolean`. */
|
|
101
101
|
emit?: boolean | ((eventType: string, data: Record<string, unknown>) => boolean);
|
|
102
|
+
/** Projection policy for this step's context window. Overrides the harness-level default. */
|
|
103
|
+
projection?: ProjectionPolicy;
|
|
102
104
|
}
|
|
103
105
|
/** @public A step that invokes a single tool directly, bypassing the LLM. */
|
|
104
106
|
export interface StepTool<_TMemory = ContextMemory, I = unknown, O = unknown> {
|
|
@@ -2,13 +2,33 @@ import type { LLMResponse } from '../types/common.js';
|
|
|
2
2
|
import type { Context } from '../types/context.js';
|
|
3
3
|
import type { InputMessageItem, Item, MessageItem } from '../types/items.js';
|
|
4
4
|
export declare function isAssistantMessage(item: unknown): item is MessageItem;
|
|
5
|
-
|
|
5
|
+
/** @public Input message item narrowed to the `user` role. */
|
|
6
|
+
export type UserMessageItem = InputMessageItem & {
|
|
7
|
+
readonly role: 'user';
|
|
8
|
+
};
|
|
9
|
+
export declare function isUserMessage(item: unknown): item is UserMessageItem;
|
|
6
10
|
export declare function isOutputText(part: {
|
|
7
11
|
type: string;
|
|
8
12
|
}): part is {
|
|
9
13
|
type: 'output_text';
|
|
10
14
|
text: string;
|
|
11
15
|
};
|
|
16
|
+
/** True for any content part carrying a string `text` field (input_text, output_text, …). */
|
|
17
|
+
export declare function isTextPart(part: {
|
|
18
|
+
type: string;
|
|
19
|
+
}): part is {
|
|
20
|
+
type: string;
|
|
21
|
+
text: string;
|
|
22
|
+
};
|
|
23
|
+
/** Extracts assistant/output text from items for memory buffering (one string per message). */
|
|
24
|
+
export declare function collectOutputText(items: ReadonlyArray<Item>): string[];
|
|
25
|
+
/**
|
|
26
|
+
* Extracts text from INPUT items for memory buffering: concatenates the text
|
|
27
|
+
* content parts of message items (user `input_text` and any text parts) and
|
|
28
|
+
* appends the `output` of `function_call_output` (tool result) items. Empty
|
|
29
|
+
* strings are dropped.
|
|
30
|
+
*/
|
|
31
|
+
export declare function collectInputText(items: ReadonlyArray<Item>): string[];
|
|
12
32
|
export declare function createMessage(text: string, role: 'user' | 'developer'): InputMessageItem;
|
|
13
33
|
export declare function extractAssistantText(items: ReadonlyArray<Item>): string;
|
|
14
34
|
/** Naive token estimate: ~4 chars per token */
|
|
@@ -21,6 +21,47 @@ export function isUserMessage(item) {
|
|
|
21
21
|
export function isOutputText(part) {
|
|
22
22
|
return part.type === 'output_text';
|
|
23
23
|
}
|
|
24
|
+
/** True for any content part carrying a string `text` field (input_text, output_text, …). */
|
|
25
|
+
export function isTextPart(part) {
|
|
26
|
+
return 'text' in part && typeof part.text === 'string';
|
|
27
|
+
}
|
|
28
|
+
/** Extracts assistant/output text from items for memory buffering (one string per message). */
|
|
29
|
+
export function collectOutputText(items) {
|
|
30
|
+
return items
|
|
31
|
+
.filter(isAssistantMessage)
|
|
32
|
+
.map((i) => i.content
|
|
33
|
+
.filter(isOutputText)
|
|
34
|
+
.map((c) => c.text)
|
|
35
|
+
.join(''))
|
|
36
|
+
.filter((t) => t.length > 0);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Extracts text from INPUT items for memory buffering: concatenates the text
|
|
40
|
+
* content parts of message items (user `input_text` and any text parts) and
|
|
41
|
+
* appends the `output` of `function_call_output` (tool result) items. Empty
|
|
42
|
+
* strings are dropped.
|
|
43
|
+
*/
|
|
44
|
+
export function collectInputText(items) {
|
|
45
|
+
const texts = [];
|
|
46
|
+
for (const item of items) {
|
|
47
|
+
if (item.type === 'message') {
|
|
48
|
+
let text = '';
|
|
49
|
+
for (const part of item.content) {
|
|
50
|
+
if (isTextPart(part)) {
|
|
51
|
+
text += part.text;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (text.length > 0) {
|
|
55
|
+
texts.push(text);
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (item.type === 'function_call_output' && item.output.length > 0) {
|
|
60
|
+
texts.push(item.output);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return texts;
|
|
64
|
+
}
|
|
24
65
|
export function createMessage(text, role) {
|
|
25
66
|
return {
|
|
26
67
|
id: crypto.randomUUID(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noetic-tools/types",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Foundational type contracts and primitives shared across the Noetic framework (items, LLM config, adapters, steering, errors).",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"scripts": {
|
|
61
61
|
"build": "bunx tsc -p tsconfig.build.json && bun scripts/add-js-extensions.ts",
|
|
62
62
|
"verify:esm": "bun scripts/verify-node-esm.ts",
|
|
63
|
-
"prepublishOnly": "rm -rf dist && bun run build && bun run verify:esm",
|
|
63
|
+
"prepublishOnly": "rm -rf dist && bun run build && bun run verify:esm && bun scripts/strip-dev-conditions.ts",
|
|
64
64
|
"test": "bun test",
|
|
65
65
|
"test:coverage": "bun test --coverage --coverage-reporter=text --coverage-reporter=lcov",
|
|
66
66
|
"test:ci": "bun test --coverage --coverage-reporter=lcov",
|