@evomap/evolver-core 2.0.0-beta.1 → 2.0.0-beta.3
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/assets/gep/genes.jsonl +5 -0
- package/dist/algo/candidateAssembly.js +23 -14
- package/dist/algo/capabilityCandidates.d.ts +2 -0
- package/dist/algo/capabilityCandidates.js +5 -0
- package/dist/algo/conversationSniffer.d.ts +18 -0
- package/dist/algo/conversationSniffer.js +132 -0
- package/dist/algo/cycleEngine.d.ts +11 -0
- package/dist/algo/cycleEngine.js +14 -6
- package/dist/algo/cycleFailureClassifier.d.ts +1 -1
- package/dist/algo/cycleFailureClassifier.js +13 -5
- package/dist/algo/geneIntake.d.ts +14 -4
- package/dist/algo/geneIntake.js +37 -9
- package/dist/algo/geneSelection.d.ts +18 -1
- package/dist/algo/geneSelection.js +42 -18
- package/dist/algo/index.d.ts +2 -0
- package/dist/algo/index.js +2 -0
- package/dist/algo/memoryGraph.d.ts +62 -0
- package/dist/algo/memoryGraph.js +86 -0
- package/dist/algo/orchestrator.d.ts +3 -0
- package/dist/algo/orchestrator.js +14 -2
- package/dist/assetstore/assetSidecarRecords.d.ts +23 -0
- package/dist/assetstore/assetSidecarRecords.js +142 -0
- package/dist/assetstore/assetSidecarRecovery.d.ts +48 -0
- package/dist/assetstore/assetSidecarRecovery.js +288 -0
- package/dist/assetstore/assetStoreHealth.d.ts +75 -0
- package/dist/assetstore/assetStoreHealth.js +277 -0
- package/dist/assetstore/assetStoreLayout.d.ts +2 -0
- package/dist/assetstore/assetStoreLayout.js +6 -0
- package/dist/assetstore/assetStoreStorage.d.ts +42 -0
- package/dist/assetstore/assetStoreStorage.js +318 -0
- package/dist/assetstore/assetSyncLedger.d.ts +32 -0
- package/dist/assetstore/assetSyncLedger.js +66 -0
- package/dist/assetstore/index.d.ts +5 -1
- package/dist/assetstore/index.js +5 -1
- package/dist/assetstore/localJsonl.d.ts +10 -2
- package/dist/assetstore/localJsonl.js +93 -37
- package/dist/assetstore/pendingSignals.js +3 -1
- package/dist/assetstore/provenance.d.ts +20 -2
- package/dist/assetstore/provenance.js +92 -56
- package/dist/assetstore/provider.d.ts +2 -0
- package/dist/assetstore/reviewFilter.js +5 -2
- package/dist/assetstore/reviewLedger.d.ts +14 -2
- package/dist/assetstore/reviewLedger.js +78 -43
- package/dist/assetstore/seedGenes.d.ts +3 -0
- package/dist/assetstore/seedGenes.js +134 -0
- package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
- package/dist/benchmark/antiGeneBenchmark.js +11 -1
- package/dist/benchmark/antiGeneImpact.d.ts +16 -0
- package/dist/benchmark/antiGeneImpact.js +34 -0
- package/dist/benchmark/antiGeneRollout.d.ts +2 -0
- package/dist/benchmark/antiGeneRollout.js +13 -1
- package/dist/benchmark/index.d.ts +2 -1
- package/dist/benchmark/index.js +2 -1
- package/dist/benchmark/triggerShift.d.ts +62 -0
- package/dist/benchmark/triggerShift.js +106 -0
- package/dist/events/eventArchive.d.ts +65 -0
- package/dist/events/eventArchive.js +343 -0
- package/dist/events/eventStore.js +2 -12
- package/dist/events/ingest.d.ts +1 -1
- package/dist/events/ingest.js +2 -0
- package/dist/events/paths.d.ts +1 -1
- package/dist/events/paths.js +2 -2
- package/dist/events/public.d.ts +3 -1
- package/dist/events/public.js +2 -1
- package/dist/events/retention.d.ts +24 -1
- package/dist/events/retention.js +133 -37
- package/dist/exec/autoExec.d.ts +6 -1
- package/dist/exec/autoExec.js +34 -0
- package/dist/exec/autonomousCycle.d.ts +2 -0
- package/dist/exec/autonomousCycle.js +5 -0
- package/dist/exec/claudeBridge.d.ts +7 -2
- package/dist/exec/claudeBridge.js +115 -14
- package/dist/exec/prompt.js +9 -0
- package/dist/exec/runnerRegistry.d.ts +64 -18
- package/dist/exec/runnerRegistry.js +341 -38
- package/dist/exec/selfPrObfuscation.d.ts +2 -1
- package/dist/exec/selfPrObfuscation.js +19 -6
- package/dist/hub/agentDirectory.d.ts +90 -0
- package/dist/hub/agentDirectory.js +104 -0
- package/dist/hub/bindings.js +23 -3
- package/dist/hub/capability.d.ts +14 -2
- package/dist/hub/fake.d.ts +4 -2
- package/dist/hub/fake.js +3 -2
- package/dist/hub/index.d.ts +1 -0
- package/dist/hub/index.js +1 -0
- package/dist/mailbox/catalog.js +3 -0
- package/dist/mailbox/ipcServer.js +2 -2
- package/dist/mailbox/store.d.ts +14 -0
- package/dist/mailbox/store.js +44 -2
- package/dist/material/consumer.js +9 -6
- package/dist/material/index.d.ts +1 -0
- package/dist/material/index.js +1 -0
- package/dist/material/materialArchive.d.ts +81 -0
- package/dist/material/materialArchive.js +466 -0
- package/dist/material/materialStore.d.ts +1 -0
- package/dist/material/materialStore.js +6 -13
- package/dist/ops/savingsCore.js +1 -2
- package/dist/ops/selfUpdate.d.ts +10 -1
- package/dist/ops/selfUpdate.js +64 -15
- package/dist/schema/common.d.ts +1 -1
- package/dist/schema/common.js +1 -1
- package/dist/schema/material.d.ts +5 -5
- package/dist/trace/trajectoryExport.js +2 -2
- package/dist/util/fileLock.d.ts +19 -2
- package/dist/util/fileLock.js +166 -31
- package/dist/wire/geneHints.d.ts +42 -1
- package/dist/wire/geneHints.js +52 -1
- package/dist/wire/index.d.ts +14 -2
- package/dist/wire/index.js +1 -1
- package/package.json +6 -1
|
@@ -28,8 +28,10 @@ export function defaultReadManifest(repoRoot) {
|
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
+
const MANIFEST_TRANSIENT_RETRY_MS = 5 * 60 * 1000;
|
|
31
32
|
// Resettable cache keyed by the reader identity, so distinct readers (e.g. per test) don't bleed into each other.
|
|
32
|
-
//
|
|
33
|
+
// Success is cached permanently for the reader. Failures remain fail-closed, but transient reads retry for a
|
|
34
|
+
// short window before stabilizing to null for the process lifetime.
|
|
33
35
|
const cache = new WeakMap();
|
|
34
36
|
/** Test/maintenance hook: drop any cached load for a reader (or all readers) so the load path re-runs. */
|
|
35
37
|
export function resetObfuscatedCache(reader) {
|
|
@@ -40,11 +42,15 @@ export function resetObfuscatedCache(reader) {
|
|
|
40
42
|
/**
|
|
41
43
|
* Load the obfuscated-file set from the manifest. Returns a normalized Set, or null when the manifest is
|
|
42
44
|
* missing / unreadable / structurally invalid (NOT an `obfuscate` array of literal string paths). null is the
|
|
43
|
-
* fail-closed signal: the caller must then reject all files.
|
|
45
|
+
* fail-closed signal: the caller must then reject all files. Success is cached per reader; failures retry briefly
|
|
46
|
+
* so startup-time transient filesystem errors do not disable self-PR until process restart.
|
|
44
47
|
*/
|
|
45
48
|
export function loadObfuscatedFiles(readManifest) {
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
const cached = cache.get(readManifest);
|
|
50
|
+
if (cached?.kind === 'loaded')
|
|
51
|
+
return cached.files;
|
|
52
|
+
if (cached?.kind === 'failed' && cached.stable)
|
|
53
|
+
return null;
|
|
48
54
|
let result;
|
|
49
55
|
try {
|
|
50
56
|
const raw = readManifest();
|
|
@@ -63,9 +69,16 @@ export function loadObfuscatedFiles(readManifest) {
|
|
|
63
69
|
result = new Set(manifest.obfuscate.map((f) => normalizeManifestPath(f)).filter(Boolean));
|
|
64
70
|
}
|
|
65
71
|
catch {
|
|
66
|
-
|
|
72
|
+
const now = Date.now();
|
|
73
|
+
const firstFailedAt = cached?.kind === 'failed' ? cached.firstFailedAt : now;
|
|
74
|
+
cache.set(readManifest, {
|
|
75
|
+
kind: 'failed',
|
|
76
|
+
firstFailedAt,
|
|
77
|
+
stable: now - firstFailedAt >= MANIFEST_TRANSIENT_RETRY_MS,
|
|
78
|
+
});
|
|
79
|
+
return null; // fail-closed: an unreadable/invalid manifest rejects everything
|
|
67
80
|
}
|
|
68
|
-
cache.set(readManifest, result);
|
|
81
|
+
cache.set(readManifest, { kind: 'loaded', files: result });
|
|
69
82
|
return result;
|
|
70
83
|
}
|
|
71
84
|
/**
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export declare const AGENT_DIRECTORY_DEFAULT_LIMIT = 20;
|
|
2
|
+
export declare const AGENT_DIRECTORY_MAX_LIMIT = 50;
|
|
3
|
+
export declare const AGENT_DIRECTORY_DEFAULT_TIMEOUT_MS = 8000;
|
|
4
|
+
export declare const AGENT_DIRECTORY_MAX_TIMEOUT_MS = 30000;
|
|
5
|
+
export declare const AGENT_DIRECTORY_MAX_QUERY_LENGTH = 500;
|
|
6
|
+
export declare const AGENT_DIRECTORY_MAX_SIGNAL_COUNT = 20;
|
|
7
|
+
export declare const AGENT_DIRECTORY_MAX_SIGNAL_LENGTH = 64;
|
|
8
|
+
export declare const AGENT_DIRECTORY_MAX_CURSOR_LENGTH = 256;
|
|
9
|
+
export declare const AGENT_DIRECTORY_MAX_AGENT_ID_LENGTH = 128;
|
|
10
|
+
export type AgentAvailability = 'online' | 'busy' | 'offline' | 'unknown';
|
|
11
|
+
export type AgentDirectorySort = 'relevance' | 'reputation' | 'recent' | 'availability';
|
|
12
|
+
export type SortOrder = 'asc' | 'desc';
|
|
13
|
+
export interface AgentSearchRequest {
|
|
14
|
+
query?: string;
|
|
15
|
+
signals?: readonly string[];
|
|
16
|
+
availability?: AgentAvailability;
|
|
17
|
+
sort?: AgentDirectorySort;
|
|
18
|
+
order?: SortOrder;
|
|
19
|
+
cursor?: string;
|
|
20
|
+
limit?: number;
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface AgentTaskDiscoveryRequest {
|
|
24
|
+
title: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
signals?: readonly string[];
|
|
27
|
+
availability?: AgentAvailability;
|
|
28
|
+
sort?: AgentDirectorySort;
|
|
29
|
+
order?: SortOrder;
|
|
30
|
+
cursor?: string;
|
|
31
|
+
limit?: number;
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
}
|
|
34
|
+
export interface AgentDirectoryEntry {
|
|
35
|
+
agentId: string;
|
|
36
|
+
displayName?: string;
|
|
37
|
+
summary?: string;
|
|
38
|
+
capabilities?: string[];
|
|
39
|
+
domains?: string[];
|
|
40
|
+
score?: number;
|
|
41
|
+
reputation?: number;
|
|
42
|
+
completedTaskCount?: number;
|
|
43
|
+
availability?: AgentAvailability;
|
|
44
|
+
lastSeenAt?: number;
|
|
45
|
+
}
|
|
46
|
+
export interface AgentProfile {
|
|
47
|
+
agentId: string;
|
|
48
|
+
displayName?: string;
|
|
49
|
+
summary?: string;
|
|
50
|
+
capabilities?: string[];
|
|
51
|
+
domains?: string[];
|
|
52
|
+
reputation?: number;
|
|
53
|
+
completedTaskCount?: number;
|
|
54
|
+
availability?: AgentAvailability;
|
|
55
|
+
lastSeenAt?: number;
|
|
56
|
+
}
|
|
57
|
+
export interface AgentDirectoryPage {
|
|
58
|
+
items: AgentDirectoryEntry[];
|
|
59
|
+
nextCursor?: string;
|
|
60
|
+
hasMore: boolean;
|
|
61
|
+
}
|
|
62
|
+
export type AgentDirectoryFailureCode = 'capability_unavailable' | 'permission_denied' | 'hub_unavailable' | 'timeout' | 'invalid_request' | 'invalid_response';
|
|
63
|
+
export interface AgentDirectoryFailure {
|
|
64
|
+
code: AgentDirectoryFailureCode;
|
|
65
|
+
retryable: boolean;
|
|
66
|
+
message?: string;
|
|
67
|
+
}
|
|
68
|
+
export type AgentDirectoryResult<T> = {
|
|
69
|
+
ok: true;
|
|
70
|
+
value: T;
|
|
71
|
+
} | {
|
|
72
|
+
ok: false;
|
|
73
|
+
error: AgentDirectoryFailure;
|
|
74
|
+
};
|
|
75
|
+
export interface AgentDirectoryCapability {
|
|
76
|
+
search(request: AgentSearchRequest): Promise<AgentDirectoryResult<AgentDirectoryPage>>;
|
|
77
|
+
getProfile(agentId: string, options?: {
|
|
78
|
+
timeoutMs?: number;
|
|
79
|
+
}): Promise<AgentDirectoryResult<AgentProfile | null>>;
|
|
80
|
+
discoverForTask(request: AgentTaskDiscoveryRequest): Promise<AgentDirectoryResult<AgentDirectoryPage>>;
|
|
81
|
+
}
|
|
82
|
+
export declare class AgentDirectoryInputError extends Error {
|
|
83
|
+
readonly code = "invalid_request";
|
|
84
|
+
}
|
|
85
|
+
export declare function normalizeAgentSearchRequest(input: AgentSearchRequest): Required<Pick<AgentSearchRequest, 'limit' | 'timeoutMs' | 'sort' | 'order'>> & AgentSearchRequest;
|
|
86
|
+
export declare function normalizeAgentTaskDiscoveryRequest(input: AgentTaskDiscoveryRequest): AgentTaskDiscoveryRequest & Required<Pick<AgentTaskDiscoveryRequest, 'limit' | 'timeoutMs' | 'sort' | 'order'>>;
|
|
87
|
+
export declare function normalizeAgentId(value: string): string;
|
|
88
|
+
export declare function normalizeAgentDirectoryTimeout(value: number | undefined): number;
|
|
89
|
+
export declare function capabilityUnavailable(message?: string): AgentDirectoryResult<never>;
|
|
90
|
+
export declare function unsupportedAgentDirectoryCapability(message?: string): AgentDirectoryCapability;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export const AGENT_DIRECTORY_DEFAULT_LIMIT = 20;
|
|
2
|
+
export const AGENT_DIRECTORY_MAX_LIMIT = 50;
|
|
3
|
+
export const AGENT_DIRECTORY_DEFAULT_TIMEOUT_MS = 8_000;
|
|
4
|
+
export const AGENT_DIRECTORY_MAX_TIMEOUT_MS = 30_000;
|
|
5
|
+
export const AGENT_DIRECTORY_MAX_QUERY_LENGTH = 500;
|
|
6
|
+
export const AGENT_DIRECTORY_MAX_SIGNAL_COUNT = 20;
|
|
7
|
+
export const AGENT_DIRECTORY_MAX_SIGNAL_LENGTH = 64;
|
|
8
|
+
export const AGENT_DIRECTORY_MAX_CURSOR_LENGTH = 256;
|
|
9
|
+
export const AGENT_DIRECTORY_MAX_AGENT_ID_LENGTH = 128;
|
|
10
|
+
export class AgentDirectoryInputError extends Error {
|
|
11
|
+
code = 'invalid_request';
|
|
12
|
+
}
|
|
13
|
+
export function normalizeAgentSearchRequest(input) {
|
|
14
|
+
const query = optionalText(input.query, 'query', AGENT_DIRECTORY_MAX_QUERY_LENGTH);
|
|
15
|
+
const signals = normalizeSignals(input.signals);
|
|
16
|
+
const availability = input.availability ? enumValue(input.availability, ['online', 'busy', 'offline', 'unknown'], 'availability') : undefined;
|
|
17
|
+
if (!query && signals.length === 0)
|
|
18
|
+
throw new AgentDirectoryInputError('query_or_signals_required');
|
|
19
|
+
return {
|
|
20
|
+
...(query ? { query } : {}),
|
|
21
|
+
...(signals.length > 0 ? { signals } : {}),
|
|
22
|
+
...(availability ? { availability } : {}),
|
|
23
|
+
...(input.cursor ? { cursor: requiredText(input.cursor, 'cursor', AGENT_DIRECTORY_MAX_CURSOR_LENGTH) } : {}),
|
|
24
|
+
limit: boundedInteger(input.limit, AGENT_DIRECTORY_DEFAULT_LIMIT, 1, AGENT_DIRECTORY_MAX_LIMIT, 'limit'),
|
|
25
|
+
timeoutMs: boundedInteger(input.timeoutMs, AGENT_DIRECTORY_DEFAULT_TIMEOUT_MS, 100, AGENT_DIRECTORY_MAX_TIMEOUT_MS, 'timeout_ms'),
|
|
26
|
+
sort: input.sort ? enumValue(input.sort, ['relevance', 'reputation', 'recent', 'availability'], 'sort') : 'relevance',
|
|
27
|
+
order: input.order ? enumValue(input.order, ['asc', 'desc'], 'order') : 'desc',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function normalizeAgentTaskDiscoveryRequest(input) {
|
|
31
|
+
const title = requiredText(input.title, 'title', AGENT_DIRECTORY_MAX_QUERY_LENGTH);
|
|
32
|
+
const description = optionalText(input.description, 'description', AGENT_DIRECTORY_MAX_QUERY_LENGTH);
|
|
33
|
+
const search = normalizeAgentSearchRequest({
|
|
34
|
+
query: [title, description].filter(Boolean).join('\n'),
|
|
35
|
+
signals: input.signals,
|
|
36
|
+
availability: input.availability,
|
|
37
|
+
sort: input.sort,
|
|
38
|
+
order: input.order,
|
|
39
|
+
cursor: input.cursor,
|
|
40
|
+
limit: input.limit,
|
|
41
|
+
timeoutMs: input.timeoutMs,
|
|
42
|
+
});
|
|
43
|
+
return { ...search, title, ...(description ? { description } : {}) };
|
|
44
|
+
}
|
|
45
|
+
export function normalizeAgentId(value) {
|
|
46
|
+
return requiredText(value, 'agent_id', AGENT_DIRECTORY_MAX_AGENT_ID_LENGTH);
|
|
47
|
+
}
|
|
48
|
+
export function normalizeAgentDirectoryTimeout(value) {
|
|
49
|
+
return boundedInteger(value, AGENT_DIRECTORY_DEFAULT_TIMEOUT_MS, 100, AGENT_DIRECTORY_MAX_TIMEOUT_MS, 'timeout_ms');
|
|
50
|
+
}
|
|
51
|
+
export function capabilityUnavailable(message = 'agent_directory_not_supported') {
|
|
52
|
+
return { ok: false, error: { code: 'capability_unavailable', retryable: false, message } };
|
|
53
|
+
}
|
|
54
|
+
export function unsupportedAgentDirectoryCapability(message) {
|
|
55
|
+
return {
|
|
56
|
+
search: async () => capabilityUnavailable(message),
|
|
57
|
+
getProfile: async () => capabilityUnavailable(message),
|
|
58
|
+
discoverForTask: async () => capabilityUnavailable(message),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function normalizeSignals(input) {
|
|
62
|
+
if (input === undefined)
|
|
63
|
+
return [];
|
|
64
|
+
if (!Array.isArray(input))
|
|
65
|
+
throw new AgentDirectoryInputError('signals_must_be_array');
|
|
66
|
+
if (input.length > AGENT_DIRECTORY_MAX_SIGNAL_COUNT)
|
|
67
|
+
throw new AgentDirectoryInputError('too_many_signals');
|
|
68
|
+
const out = [];
|
|
69
|
+
for (const value of input) {
|
|
70
|
+
if (typeof value !== 'string')
|
|
71
|
+
throw new AgentDirectoryInputError('signal_must_be_string');
|
|
72
|
+
const signal = requiredText(value, 'signal', AGENT_DIRECTORY_MAX_SIGNAL_LENGTH);
|
|
73
|
+
if (!out.includes(signal))
|
|
74
|
+
out.push(signal);
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
function optionalText(value, field, maxLength) {
|
|
79
|
+
if (value === undefined)
|
|
80
|
+
return undefined;
|
|
81
|
+
return requiredText(value, field, maxLength);
|
|
82
|
+
}
|
|
83
|
+
function requiredText(value, field, maxLength) {
|
|
84
|
+
if (typeof value !== 'string')
|
|
85
|
+
throw new AgentDirectoryInputError(`${field}_must_be_string`);
|
|
86
|
+
const text = value.trim();
|
|
87
|
+
if (!text)
|
|
88
|
+
throw new AgentDirectoryInputError(`${field}_required`);
|
|
89
|
+
if (text.length > maxLength)
|
|
90
|
+
throw new AgentDirectoryInputError(`${field}_too_long`);
|
|
91
|
+
return text;
|
|
92
|
+
}
|
|
93
|
+
function boundedInteger(value, fallback, min, max, field) {
|
|
94
|
+
if (value === undefined)
|
|
95
|
+
return fallback;
|
|
96
|
+
if (!Number.isInteger(value) || value < min || value > max)
|
|
97
|
+
throw new AgentDirectoryInputError(`${field}_out_of_range`);
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
function enumValue(value, allowed, field) {
|
|
101
|
+
if (!allowed.includes(value))
|
|
102
|
+
throw new AgentDirectoryInputError(`${field}_invalid`);
|
|
103
|
+
return value;
|
|
104
|
+
}
|
package/dist/hub/bindings.js
CHANGED
|
@@ -19,7 +19,7 @@ function envelopeToAgentEvent(e) {
|
|
|
19
19
|
const stableId = e.type === 'proxy_trace' && e.idempotencyKey ? e.idempotencyKey : e.id;
|
|
20
20
|
return {
|
|
21
21
|
id: stableId,
|
|
22
|
-
type: e.type,
|
|
22
|
+
type: e.type === 'dm_outbound' ? 'dm' : e.type,
|
|
23
23
|
payload: e.payload,
|
|
24
24
|
priority: 'medium',
|
|
25
25
|
createdAt: e.createdAt,
|
|
@@ -59,10 +59,20 @@ export function makeHubBindings(cap, options = {}) {
|
|
|
59
59
|
throw new PublishRejectedError(r.status, r.terminal ?? true, r.reason);
|
|
60
60
|
return r;
|
|
61
61
|
}
|
|
62
|
-
case 'task_claim':
|
|
62
|
+
case 'task_claim': {
|
|
63
|
+
const p = e.payload;
|
|
64
|
+
return cap.task.claim(firstString(p.taskId, p.task_id) ?? '');
|
|
65
|
+
}
|
|
63
66
|
case 'task_complete': {
|
|
64
67
|
const p = e.payload;
|
|
65
|
-
|
|
68
|
+
const taskId = firstString(p.taskId, p.task_id);
|
|
69
|
+
const assetId = firstString(p.assetId, p.asset_id);
|
|
70
|
+
const claimId = firstString(p.claimId, p.claim_id);
|
|
71
|
+
if (!claimId) {
|
|
72
|
+
throw new PublishRejectedError('invalid_task_complete', true, 'claim_id is required because task claim handles are opaque');
|
|
73
|
+
}
|
|
74
|
+
const context = taskId && assetId ? { taskId, assetId } : undefined;
|
|
75
|
+
return cap.task.complete(claimId, p.result, context);
|
|
66
76
|
}
|
|
67
77
|
default:
|
|
68
78
|
// 其余 outbound(atp_*/heartbeat/...) 走 mailbox.push
|
|
@@ -87,4 +97,14 @@ export function makeHubBindings(cap, options = {}) {
|
|
|
87
97
|
};
|
|
88
98
|
},
|
|
89
99
|
};
|
|
100
|
+
}
|
|
101
|
+
function firstString(...values) {
|
|
102
|
+
for (const value of values) {
|
|
103
|
+
if (typeof value !== 'string')
|
|
104
|
+
continue;
|
|
105
|
+
const trimmed = value.trim();
|
|
106
|
+
if (trimmed)
|
|
107
|
+
return trimmed;
|
|
108
|
+
}
|
|
109
|
+
return undefined;
|
|
90
110
|
}
|
package/dist/hub/capability.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AssetRecord, AssetKind, SearchQuery, PutResult } from '../assetstore/provider.js';
|
|
2
2
|
import type { Envelope } from '../mailbox/envelope.js';
|
|
3
|
+
import type { AgentDirectoryCapability } from './agentDirectory.js';
|
|
3
4
|
/** publish 投递回执. hub 侧做 gate(经济/治理/RBAC 全在 hub), core 只读 status+reason. */
|
|
4
5
|
export interface PublishReceipt {
|
|
5
6
|
/** hub 返回的回执 id(公版=AgentEvent/decision id; 私有=审计 id). */
|
|
@@ -34,6 +35,15 @@ export interface TaskEvent {
|
|
|
34
35
|
priority: 'high' | 'medium' | 'low';
|
|
35
36
|
createdAt: number;
|
|
36
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Explicit task identity needed by hubs whose completion wire does not use their
|
|
40
|
+
* adapter-specific claim handle. claimId remains opaque and must never be parsed
|
|
41
|
+
* to recover either field.
|
|
42
|
+
*/
|
|
43
|
+
export interface TaskCompleteContext {
|
|
44
|
+
taskId: string;
|
|
45
|
+
assetId: string;
|
|
46
|
+
}
|
|
37
47
|
/** Proactive Hub question that a runtime may submit for ecosystem help. */
|
|
38
48
|
export interface HubQuestion {
|
|
39
49
|
question: string;
|
|
@@ -153,7 +163,7 @@ export interface RecipeCapability {
|
|
|
153
163
|
get(recipeId: string): Promise<RecipeFetchReceipt>;
|
|
154
164
|
express(recipeId: string, request?: RecipeExpressRequest): Promise<RecipeExpressionReceipt>;
|
|
155
165
|
}
|
|
156
|
-
export type HubCapabilityName = 'publish' | 'fetch' | 'search' | 'task' | 'mailbox' | 'auth' | 'audit' | 'air_gap' | 'tenant_isolation' | 'marketplace' | 'economy' | 'questions' | 'recipes';
|
|
166
|
+
export type HubCapabilityName = 'publish' | 'fetch' | 'search' | 'task' | 'mailbox' | 'auth' | 'audit' | 'air_gap' | 'tenant_isolation' | 'marketplace' | 'economy' | 'questions' | 'recipes' | 'agent_directory';
|
|
157
167
|
/** hub 能力清单(可选). core 据此动态适配 UI/行为, "按需耦合". */
|
|
158
168
|
export interface HubManifest {
|
|
159
169
|
capabilities: HubCapabilityName[] | string[];
|
|
@@ -232,7 +242,7 @@ export interface HubCapability {
|
|
|
232
242
|
claim(taskId: string): Promise<{
|
|
233
243
|
claimId: string;
|
|
234
244
|
}>;
|
|
235
|
-
complete(claimId: string, result: unknown): Promise<{
|
|
245
|
+
complete(claimId: string, result: unknown, context?: TaskCompleteContext): Promise<{
|
|
236
246
|
status: 'completed';
|
|
237
247
|
}>;
|
|
238
248
|
subscribe(filter: unknown): AsyncIterable<TaskEvent>;
|
|
@@ -243,6 +253,8 @@ export interface HubCapability {
|
|
|
243
253
|
};
|
|
244
254
|
/** Optional recipe/DNA seam. Recipe creation is draft-first; publishing remains explicit. */
|
|
245
255
|
recipes?: RecipeCapability;
|
|
256
|
+
/** Optional agent discovery seam. Missing means the adapter does not support directory/profile discovery. */
|
|
257
|
+
agentDirectory?: AgentDirectoryCapability;
|
|
246
258
|
/** mailbox 队列. poll/ack/push/status 四端点解耦(公版 /a2a/mailbox/*). */
|
|
247
259
|
mailbox: {
|
|
248
260
|
poll(): Promise<MailboxPollResult>;
|
package/dist/hub/fake.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HubCapability, PublishReceipt, AgentEvent, TaskEvent, AuthProvider, HubQuery, AssetRecord } from './capability.js';
|
|
1
|
+
import type { HubCapability, PublishReceipt, AgentEvent, TaskEvent, AuthProvider, HubQuery, AssetRecord, TaskCompleteContext } from './capability.js';
|
|
2
2
|
export interface FakeHubOptions {
|
|
3
3
|
/** 脚本化 publish gate: 返回 reject/quarantine 以测 PublishReceipt 终态路径. */
|
|
4
4
|
publishGate?: (asset: AssetRecord) => Pick<PublishReceipt, 'status' | 'reason' | 'terminal'>;
|
|
@@ -20,8 +20,10 @@ export declare class FakeHubCapability implements HubCapability {
|
|
|
20
20
|
readonly completed: Array<{
|
|
21
21
|
claimId: string;
|
|
22
22
|
result: unknown;
|
|
23
|
+
context?: TaskCompleteContext;
|
|
23
24
|
}>;
|
|
24
25
|
readonly auth: AuthProvider;
|
|
26
|
+
agentDirectory?: import('./agentDirectory.js').AgentDirectoryCapability;
|
|
25
27
|
nextPollAfterMs: number | undefined;
|
|
26
28
|
constructor(opts?: FakeHubOptions);
|
|
27
29
|
/** 测试注入: 排一条 inbound 事件供 poll 拉. */
|
|
@@ -33,7 +35,7 @@ export declare class FakeHubCapability implements HubCapability {
|
|
|
33
35
|
claim: (taskId: string) => Promise<{
|
|
34
36
|
claimId: string;
|
|
35
37
|
}>;
|
|
36
|
-
complete: (claimId: string, result: unknown) => Promise<{
|
|
38
|
+
complete: (claimId: string, result: unknown, context?: TaskCompleteContext) => Promise<{
|
|
37
39
|
status: "completed";
|
|
38
40
|
}>;
|
|
39
41
|
subscribe: (filter: unknown) => AsyncIterable<TaskEvent>;
|
package/dist/hub/fake.js
CHANGED
|
@@ -19,6 +19,7 @@ export class FakeHubCapability {
|
|
|
19
19
|
claims = [];
|
|
20
20
|
completed = [];
|
|
21
21
|
auth = fakeAuth;
|
|
22
|
+
agentDirectory;
|
|
22
23
|
nextPollAfterMs;
|
|
23
24
|
constructor(opts = {}) {
|
|
24
25
|
this.opts = opts;
|
|
@@ -42,8 +43,8 @@ export class FakeHubCapability {
|
|
|
42
43
|
this.claims.push({ taskId, claimId });
|
|
43
44
|
return { claimId };
|
|
44
45
|
},
|
|
45
|
-
complete: async (claimId, result) => {
|
|
46
|
-
this.completed.push({ claimId, result });
|
|
46
|
+
complete: async (claimId, result, context) => {
|
|
47
|
+
this.completed.push({ claimId, result, ...(context ? { context } : {}) });
|
|
47
48
|
return { status: 'completed' };
|
|
48
49
|
},
|
|
49
50
|
subscribe: async function* () {
|
package/dist/hub/index.d.ts
CHANGED
package/dist/hub/index.js
CHANGED
package/dist/mailbox/catalog.js
CHANGED
|
@@ -14,6 +14,7 @@ export const MESSAGE_CATALOG = {
|
|
|
14
14
|
task_complete: { direction: 'outbound', handler: 'proxy', feedsMaterial: false, ttlClass: 'default' },
|
|
15
15
|
task_subscribe: { direction: 'outbound', handler: 'proxy', feedsMaterial: false, ttlClass: 'control' },
|
|
16
16
|
task_unsubscribe: { direction: 'outbound', handler: 'proxy', feedsMaterial: false, ttlClass: 'control' },
|
|
17
|
+
dm_outbound: { direction: 'outbound', handler: 'proxy', feedsMaterial: false, ttlClass: 'default' },
|
|
17
18
|
atp_order: { direction: 'outbound', handler: 'proxy', feedsMaterial: false, ttlClass: 'default' },
|
|
18
19
|
atp_deliver: { direction: 'outbound', handler: 'proxy', feedsMaterial: false, ttlClass: 'default' },
|
|
19
20
|
atp_settle: { direction: 'outbound', handler: 'proxy', feedsMaterial: false, ttlClass: 'default' },
|
|
@@ -23,6 +24,8 @@ export const MESSAGE_CATALOG = {
|
|
|
23
24
|
asset_publish_result: { direction: 'inbound', handler: 'core', feedsMaterial: false, ttlClass: 'default' }, // 拒绝进 Material 在 dispatch 决定
|
|
24
25
|
pending_evomap_task: { direction: 'inbound', handler: 'agent', feedsMaterial: false, ttlClass: 'default' },
|
|
25
26
|
task_available: { direction: 'inbound', handler: 'agent', feedsMaterial: false, ttlClass: 'default' },
|
|
27
|
+
task_claim_result: { direction: 'inbound', handler: 'agent', feedsMaterial: false, ttlClass: 'default' },
|
|
28
|
+
task_complete_result: { direction: 'inbound', handler: 'agent', feedsMaterial: false, ttlClass: 'default' },
|
|
26
29
|
work_assigned: { direction: 'inbound', handler: 'agent', feedsMaterial: false, ttlClass: 'default' },
|
|
27
30
|
dialog_message: { direction: 'inbound', handler: 'agent', feedsMaterial: true, ttlClass: 'default' },
|
|
28
31
|
peer_review_request: { direction: 'inbound', handler: 'agent', feedsMaterial: true, ttlClass: 'default' },
|
|
@@ -69,7 +69,7 @@ export class MailboxIpcServer {
|
|
|
69
69
|
this.onAuthFailure?.();
|
|
70
70
|
}
|
|
71
71
|
catch { /* auth failure handling must never weaken fail-closed IPC auth */ }
|
|
72
|
-
return this.json(res, 401, { error: '
|
|
72
|
+
return this.json(res, 401, { error: 'Unauthorized', code: 'unauthorized' });
|
|
73
73
|
}
|
|
74
74
|
const url = new URL(req.url ?? '/', 'http://localhost');
|
|
75
75
|
const route = `${req.method} ${url.pathname}`;
|
|
@@ -129,7 +129,7 @@ export class MailboxIpcServer {
|
|
|
129
129
|
return this.json(res, 404, { error: 'unknown route' });
|
|
130
130
|
}
|
|
131
131
|
catch (e) {
|
|
132
|
-
return this.json(res, 400, { error: e instanceof Error ? e.message : String(e) });
|
|
132
|
+
return this.json(res, 400, { error: e instanceof Error ? e.message : String(e), code: 'invalid_request' });
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
readJson(req) {
|
package/dist/mailbox/store.d.ts
CHANGED
|
@@ -22,8 +22,22 @@ export declare class MailboxStore {
|
|
|
22
22
|
status?: Status;
|
|
23
23
|
handler?: string;
|
|
24
24
|
runtimeNamespace?: string;
|
|
25
|
+
type?: string;
|
|
26
|
+
direction?: Envelope['direction'];
|
|
27
|
+
typeDirections?: readonly {
|
|
28
|
+
type: string;
|
|
29
|
+
direction: Envelope['direction'];
|
|
30
|
+
}[];
|
|
31
|
+
newestFirst?: boolean;
|
|
32
|
+
offset?: number;
|
|
25
33
|
limit?: number;
|
|
26
34
|
}): Envelope[];
|
|
35
|
+
countMessages(opts?: {
|
|
36
|
+
status?: Status;
|
|
37
|
+
runtimeNamespace?: string;
|
|
38
|
+
type?: string;
|
|
39
|
+
direction?: Envelope['direction'];
|
|
40
|
+
}): number;
|
|
27
41
|
countByStatus(status: Status): number;
|
|
28
42
|
/** pending 计数(可按 handler/runtimeNamespace 分区), 用于 agent wake 去抖. */
|
|
29
43
|
countPending(handler?: string, runtimeNamespace?: string): number;
|
package/dist/mailbox/store.js
CHANGED
|
@@ -120,6 +120,7 @@ export class MailboxStore {
|
|
|
120
120
|
schemaVersion TEXT, feedsMaterial INTEGER, dlq INTEGER DEFAULT 0, leasedUntil INTEGER, workerId TEXT, lastError TEXT)`);
|
|
121
121
|
this.db.exec('CREATE INDEX IF NOT EXISTS idx_status ON messages(status, handler)');
|
|
122
122
|
this.db.exec('CREATE INDEX IF NOT EXISTS idx_corr ON messages(correlationId)');
|
|
123
|
+
this.db.exec('CREATE INDEX IF NOT EXISTS idx_message_view ON messages(runtimeNamespace,type,direction,status,createdAt)');
|
|
123
124
|
this.db.exec('CREATE TABLE IF NOT EXISTS idempotency (key TEXT PRIMARY KEY, result TEXT, at INTEGER)');
|
|
124
125
|
this.db.exec('CREATE TABLE IF NOT EXISTS kv (k TEXT PRIMARY KEY, v TEXT)');
|
|
125
126
|
}
|
|
@@ -157,7 +158,21 @@ export class MailboxStore {
|
|
|
157
158
|
where.push('runtimeNamespace = ?');
|
|
158
159
|
args.push(opts.runtimeNamespace);
|
|
159
160
|
}
|
|
160
|
-
|
|
161
|
+
if (opts.type) {
|
|
162
|
+
where.push('type = ?');
|
|
163
|
+
args.push(opts.type);
|
|
164
|
+
}
|
|
165
|
+
if (opts.direction) {
|
|
166
|
+
where.push('direction = ?');
|
|
167
|
+
args.push(opts.direction);
|
|
168
|
+
}
|
|
169
|
+
if (opts.typeDirections && opts.typeDirections.length > 0) {
|
|
170
|
+
const selectors = opts.typeDirections.slice(0, 20);
|
|
171
|
+
where.push(`(${selectors.map(() => '(type = ? AND direction = ?)').join(' OR ')})`);
|
|
172
|
+
for (const selector of selectors)
|
|
173
|
+
args.push(selector.type, selector.direction);
|
|
174
|
+
}
|
|
175
|
+
const sql = `SELECT * FROM messages ${where.length ? `WHERE ${where.join(' AND ')}` : ''} ORDER BY createdAt ${opts.newestFirst ? 'DESC' : 'ASC'} LIMIT ?`;
|
|
161
176
|
// Clamp the LIMIT bind. The IPC list route feeds this `?limit=` straight from an external query string, so a
|
|
162
177
|
// non-finite value (NaN from `?limit=abc`, Infinity from `?limit=1e400`) would bind as null/error and a huge
|
|
163
178
|
// finite value is an unbounded read (memory DoS) even on the token-authed endpoint. Finite + positive + capped.
|
|
@@ -165,7 +180,34 @@ export class MailboxStore {
|
|
|
165
180
|
? Math.min(Math.floor(opts.limit), 10_000)
|
|
166
181
|
: 1000;
|
|
167
182
|
args.push(lim);
|
|
168
|
-
|
|
183
|
+
const offset = Number.isFinite(opts.offset) && opts.offset > 0
|
|
184
|
+
? Math.min(Math.floor(opts.offset), 10_000)
|
|
185
|
+
: 0;
|
|
186
|
+
const pagedSql = `${sql} OFFSET ?`;
|
|
187
|
+
args.push(offset);
|
|
188
|
+
return this.db.prepare(pagedSql).all(...args).map(rowToEnvelope);
|
|
189
|
+
}
|
|
190
|
+
countMessages(opts = {}) {
|
|
191
|
+
const where = [];
|
|
192
|
+
const args = [];
|
|
193
|
+
if (opts.status) {
|
|
194
|
+
where.push('status = ?');
|
|
195
|
+
args.push(opts.status);
|
|
196
|
+
}
|
|
197
|
+
if (opts.runtimeNamespace) {
|
|
198
|
+
where.push('runtimeNamespace = ?');
|
|
199
|
+
args.push(opts.runtimeNamespace);
|
|
200
|
+
}
|
|
201
|
+
if (opts.type) {
|
|
202
|
+
where.push('type = ?');
|
|
203
|
+
args.push(opts.type);
|
|
204
|
+
}
|
|
205
|
+
if (opts.direction) {
|
|
206
|
+
where.push('direction = ?');
|
|
207
|
+
args.push(opts.direction);
|
|
208
|
+
}
|
|
209
|
+
const row = this.db.prepare(`SELECT COUNT(*) c FROM messages ${where.length ? `WHERE ${where.join(' AND ')}` : ''}`).get(...args);
|
|
210
|
+
return Number(row['c']);
|
|
169
211
|
}
|
|
170
212
|
countByStatus(status) {
|
|
171
213
|
return Number(this.db.prepare('SELECT COUNT(*) c FROM messages WHERE status = ?').get(status)['c']);
|
|
@@ -11,9 +11,10 @@ export class ConsumerGroups {
|
|
|
11
11
|
if (opts.path && existsSync(opts.path)) {
|
|
12
12
|
try {
|
|
13
13
|
const o = JSON.parse(readFileSync(opts.path, 'utf8'));
|
|
14
|
-
for (const [k, v] of Object.entries(o))
|
|
15
|
-
if (typeof v === 'number' && Number.
|
|
14
|
+
for (const [k, v] of Object.entries(o)) {
|
|
15
|
+
if (typeof v === 'number' && Number.isSafeInteger(v) && v >= 0)
|
|
16
16
|
this.committed.set(k, v);
|
|
17
|
+
}
|
|
17
18
|
}
|
|
18
19
|
catch { /* corrupt cursor → start from 0 (idempotent re-claim) */ }
|
|
19
20
|
}
|
|
@@ -21,16 +22,18 @@ export class ConsumerGroups {
|
|
|
21
22
|
cur(group) { return this.committed.get(group) ?? 0; }
|
|
22
23
|
/** 取下一批未 ack 的 (不推进 committed → 崩溃重启再 claim 拿到同批). */
|
|
23
24
|
claim(group, batchSize) {
|
|
24
|
-
|
|
25
|
-
return all.slice(this.cur(group), this.cur(group) + batchSize);
|
|
25
|
+
return this.opts.store.readRange(this.cur(group), batchSize);
|
|
26
26
|
}
|
|
27
27
|
/** ack: 把 committed 推过 acked 的连续前缀. */
|
|
28
28
|
ack(group, materialIds) {
|
|
29
29
|
const ackSet = new Set(materialIds);
|
|
30
|
-
const all = this.opts.store.readAll();
|
|
31
30
|
let c = this.cur(group);
|
|
32
|
-
|
|
31
|
+
const claimed = this.opts.store.readRange(c, ackSet.size);
|
|
32
|
+
for (const record of claimed) {
|
|
33
|
+
if (!ackSet.has(record.materialId))
|
|
34
|
+
break;
|
|
33
35
|
c += 1;
|
|
36
|
+
}
|
|
34
37
|
this.committed.set(group, c);
|
|
35
38
|
this.persist();
|
|
36
39
|
}
|
package/dist/material/index.d.ts
CHANGED
package/dist/material/index.js
CHANGED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { type Material } from '../schema/material.js';
|
|
2
|
+
export declare const MATERIAL_ARCHIVE_DEFAULT_KEEP_RECORDS = 1000;
|
|
3
|
+
export declare class InvalidMaterialLogError extends Error {
|
|
4
|
+
readonly line: number;
|
|
5
|
+
readonly code = "MATERIAL_ARCHIVE_INVALID_LOG";
|
|
6
|
+
constructor(line: number, reason: string);
|
|
7
|
+
}
|
|
8
|
+
export declare class InvalidMaterialArchiveError extends Error {
|
|
9
|
+
readonly segment: string;
|
|
10
|
+
readonly line: number;
|
|
11
|
+
readonly code = "MATERIAL_ARCHIVE_INVALID_ARCHIVE";
|
|
12
|
+
constructor(segment: string, line: number, reason: string);
|
|
13
|
+
}
|
|
14
|
+
export declare class MaterialArchiveRangeError extends Error {
|
|
15
|
+
readonly code = "MATERIAL_ARCHIVE_RANGE_INVALID";
|
|
16
|
+
constructor(reason: string);
|
|
17
|
+
}
|
|
18
|
+
export declare class MaterialArchiveSegmentConflictError extends Error {
|
|
19
|
+
readonly archiveId: string;
|
|
20
|
+
readonly code = "MATERIAL_ARCHIVE_SEGMENT_CONFLICT";
|
|
21
|
+
constructor(archiveId: string);
|
|
22
|
+
}
|
|
23
|
+
export declare class InvalidMaterialArchiveCursorError extends Error {
|
|
24
|
+
readonly code = "MATERIAL_ARCHIVE_CURSOR_INVALID";
|
|
25
|
+
constructor(reason: string);
|
|
26
|
+
}
|
|
27
|
+
export interface MaterialArchiveOptions {
|
|
28
|
+
path: string;
|
|
29
|
+
cursorPaths?: readonly string[];
|
|
30
|
+
keepRecords?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface MaterialArchiveCursor {
|
|
33
|
+
group: string;
|
|
34
|
+
position: number;
|
|
35
|
+
}
|
|
36
|
+
export interface MaterialArchivePlan {
|
|
37
|
+
mode: 'preview';
|
|
38
|
+
activeRecords: number;
|
|
39
|
+
physicalActiveRecords: number;
|
|
40
|
+
archiveRecords: number;
|
|
41
|
+
historyRecords: number;
|
|
42
|
+
keepRecords: number;
|
|
43
|
+
minCursor: number;
|
|
44
|
+
cursors: MaterialArchiveCursor[];
|
|
45
|
+
overlapRecords: number;
|
|
46
|
+
wouldArchive: number;
|
|
47
|
+
retainedRecords: number;
|
|
48
|
+
archiveId: string | null;
|
|
49
|
+
}
|
|
50
|
+
export interface MaterialArchiveResult {
|
|
51
|
+
mode: 'write';
|
|
52
|
+
activeRecordsBefore: number;
|
|
53
|
+
physicalActiveRecordsBefore: number;
|
|
54
|
+
keepRecords: number;
|
|
55
|
+
minCursor: number;
|
|
56
|
+
cursors: MaterialArchiveCursor[];
|
|
57
|
+
archivedRecords: number;
|
|
58
|
+
retainedRecords: number;
|
|
59
|
+
archiveRecords: number;
|
|
60
|
+
historyRecords: number;
|
|
61
|
+
recoveredOverlap: number;
|
|
62
|
+
archiveId: string | null;
|
|
63
|
+
}
|
|
64
|
+
export interface MaterialArchiveStats {
|
|
65
|
+
exists: boolean;
|
|
66
|
+
segments: number;
|
|
67
|
+
bytes: number;
|
|
68
|
+
records: number;
|
|
69
|
+
invalidLines: number;
|
|
70
|
+
firstOffset: number | null;
|
|
71
|
+
lastOffset: number | null;
|
|
72
|
+
}
|
|
73
|
+
export declare function materialArchiveDir(path: string): string;
|
|
74
|
+
export declare function materialArchiveSegmentName(start: number, end: number): string;
|
|
75
|
+
export declare function planMaterialArchive(opts: MaterialArchiveOptions): MaterialArchivePlan;
|
|
76
|
+
export declare function archiveMaterialStore(opts: MaterialArchiveOptions): MaterialArchiveResult;
|
|
77
|
+
/** Operational, fail-soft full history reader. Strict archive writes validate every line and range. */
|
|
78
|
+
export declare function readMaterialHistory(path: string): Material[];
|
|
79
|
+
/** Absolute-offset range reader used by durable consumers after active records move into archive segments. */
|
|
80
|
+
export declare function readMaterialRange(path: string, start: number, count: number): Material[];
|
|
81
|
+
export declare function inspectMaterialArchive(path: string): MaterialArchiveStats;
|