@devflow-tools/database 0.17.8 → 0.18.1
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/dist/data/LocalDataProvider.d.ts +63 -0
- package/dist/data/LocalDataProvider.js +332 -0
- package/dist/data/default-enforcer-rules.d.ts +3 -0
- package/dist/data/default-enforcer-rules.js +170 -0
- package/dist/data/devflow-schema.d.ts +2 -0
- package/dist/data/devflow-schema.js +80 -0
- package/dist/database.d.ts +45 -7
- package/dist/database.js +583 -125
- package/dist/index.d.ts +8 -4
- package/dist/index.js +11 -5
- package/dist/retrieval-ledger.d.ts +16 -1
- package/dist/retrieval-ledger.js +6 -0
- package/dist/retrieval-runtime.d.ts +39 -0
- package/dist/retrieval-runtime.js +2 -0
- package/dist/task-aggregate.d.ts +49 -0
- package/dist/task-aggregate.js +2 -0
- package/dist/task-semantic-control.d.ts +8 -1
- package/dist/task-semantic-control.js +2 -4
- package/package.json +15 -3
- package/CHANGELOG.md +0 -844
- package/__tests__/database.failure-category.test.ts +0 -44
- package/__tests__/database.host-actions.test.ts +0 -405
- package/__tests__/database.learning-candidates.test.ts +0 -93
- package/__tests__/database.memory-turn-receipts.test.ts +0 -98
- package/__tests__/database.retrieval-ledger.test.ts +0 -68
- package/__tests__/database.retrieval-sessions.test.ts +0 -79
- package/__tests__/database.semantic-resolution.test.ts +0 -87
- package/__tests__/database.skill-executions.test.ts +0 -456
- package/__tests__/database.task-runtime.test.ts +0 -73
- package/__tests__/database.test.ts +0 -177
- package/__tests__/database.work-queue.test.ts +0 -274
- package/__tests__/database.workflow-workers.test.ts +0 -60
- package/__tests__/node-sqlite.test.ts +0 -68
- package/src/database.ts +0 -5853
- package/src/host-actions.ts +0 -211
- package/src/index.ts +0 -153
- package/src/learning-candidates.ts +0 -181
- package/src/node-sqlite.ts +0 -60
- package/src/obligation-ledger.ts +0 -57
- package/src/retrieval-ledger.ts +0 -35
- package/src/retrieval-sessions.ts +0 -196
- package/src/semantic-resolution.ts +0 -181
- package/src/task-runtime.ts +0 -169
- package/src/task-semantic-control.ts +0 -270
- package/src/types.ts +0 -17
- package/src/work-queue.ts +0 -123
- package/src/workflow-workers.ts +0 -198
- package/tsconfig.json +0 -19
- package/tsconfig.tsbuildinfo +0 -1
- package/vitest.config.ts +0 -41
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
export const RETRIEVAL_MAX_CYCLES = 3 as const;
|
|
2
|
-
|
|
3
|
-
export type RetrievalSessionState = 'open' | 'satisfied' | 'exhausted' | 'expired';
|
|
4
|
-
|
|
5
|
-
export type RetrievalGapKind =
|
|
6
|
-
| 'target'
|
|
7
|
-
| 'caller'
|
|
8
|
-
| 'route'
|
|
9
|
-
| 'resource'
|
|
10
|
-
| 'test'
|
|
11
|
-
| 'configuration'
|
|
12
|
-
| 'symbol_ambiguity'
|
|
13
|
-
| 'memory_applicability'
|
|
14
|
-
| 'knowledge_version'
|
|
15
|
-
| 'runtime_evidence'
|
|
16
|
-
| 'unavailable_channel';
|
|
17
|
-
|
|
18
|
-
export interface RetrievalGapRecord {
|
|
19
|
-
kind: RetrievalGapKind;
|
|
20
|
-
target?: string;
|
|
21
|
-
reason: string;
|
|
22
|
-
evidence: string[];
|
|
23
|
-
resolver: 'codegraph' | 'memory' | 'knowledge' | 'analyzer' | 'none';
|
|
24
|
-
priority: number;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface RetrievalCycleRecord {
|
|
28
|
-
retrievalSessionId: string;
|
|
29
|
-
cycle: number;
|
|
30
|
-
gaps: RetrievalGapRecord[];
|
|
31
|
-
selectedIds: string[];
|
|
32
|
-
rejectedIds: string[];
|
|
33
|
-
tokenCost: number;
|
|
34
|
-
remainingTokenBudget: number;
|
|
35
|
-
quality: Record<string, unknown>;
|
|
36
|
-
receipt: string;
|
|
37
|
-
evidenceHash: string;
|
|
38
|
-
createdAt: number;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface RetrievalSessionRecord {
|
|
42
|
-
id: string;
|
|
43
|
-
requestId: string;
|
|
44
|
-
projectRoot: string;
|
|
45
|
-
sessionId: string;
|
|
46
|
-
executionId?: string;
|
|
47
|
-
query: string;
|
|
48
|
-
intent: string;
|
|
49
|
-
state: RetrievalSessionState;
|
|
50
|
-
cycle: number;
|
|
51
|
-
maxCycles: typeof RETRIEVAL_MAX_CYCLES;
|
|
52
|
-
initialTokenBudget: number;
|
|
53
|
-
remainingTokenBudget: number;
|
|
54
|
-
baselineReceipt: string;
|
|
55
|
-
finalReceipt?: string;
|
|
56
|
-
createdAt: number;
|
|
57
|
-
updatedAt: number;
|
|
58
|
-
expiresAt: number;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export interface CreateRetrievalSessionInput {
|
|
62
|
-
id?: string;
|
|
63
|
-
requestId: string;
|
|
64
|
-
projectRoot: string;
|
|
65
|
-
sessionId: string;
|
|
66
|
-
executionId?: string;
|
|
67
|
-
query: string;
|
|
68
|
-
intent: string;
|
|
69
|
-
tokenBudget: number;
|
|
70
|
-
baselineReceipt: string;
|
|
71
|
-
expiresAt: number;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export interface AppendRetrievalCycleInput {
|
|
75
|
-
retrievalSessionId: string;
|
|
76
|
-
projectRoot: string;
|
|
77
|
-
sessionId: string;
|
|
78
|
-
baselineReceipt: string;
|
|
79
|
-
cycle: number;
|
|
80
|
-
gaps: RetrievalGapRecord[];
|
|
81
|
-
selectedIds: string[];
|
|
82
|
-
rejectedIds: string[];
|
|
83
|
-
tokenCost: number;
|
|
84
|
-
remainingTokenBudget: number;
|
|
85
|
-
quality: Record<string, unknown>;
|
|
86
|
-
receipt: string;
|
|
87
|
-
evidenceHash: string;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function mapRetrievalSessionRow(row: Record<string, unknown>): RetrievalSessionRecord {
|
|
91
|
-
const state = requiredString(row.state, 'state');
|
|
92
|
-
if (!isRetrievalSessionState(state)) {
|
|
93
|
-
throw new Error(`Invalid persisted retrieval session state: ${state}`);
|
|
94
|
-
}
|
|
95
|
-
const maxCycles = finiteInteger(row.max_cycles, 'max_cycles');
|
|
96
|
-
if (maxCycles !== RETRIEVAL_MAX_CYCLES) {
|
|
97
|
-
throw new Error(`Invalid persisted retrieval max_cycles: ${maxCycles}`);
|
|
98
|
-
}
|
|
99
|
-
return {
|
|
100
|
-
id: requiredString(row.id, 'id'),
|
|
101
|
-
requestId: requiredString(row.request_id, 'request_id'),
|
|
102
|
-
projectRoot: requiredString(row.project_root, 'project_root'),
|
|
103
|
-
sessionId: requiredString(row.session_id, 'session_id'),
|
|
104
|
-
executionId: optionalString(row.execution_id),
|
|
105
|
-
query: requiredString(row.query, 'query'),
|
|
106
|
-
intent: requiredString(row.intent, 'intent'),
|
|
107
|
-
state,
|
|
108
|
-
cycle: finiteInteger(row.cycle, 'cycle'),
|
|
109
|
-
maxCycles: RETRIEVAL_MAX_CYCLES,
|
|
110
|
-
initialTokenBudget: finiteInteger(row.initial_token_budget, 'initial_token_budget'),
|
|
111
|
-
remainingTokenBudget: finiteInteger(row.remaining_token_budget, 'remaining_token_budget'),
|
|
112
|
-
baselineReceipt: requiredString(row.baseline_receipt, 'baseline_receipt'),
|
|
113
|
-
finalReceipt: optionalString(row.final_receipt),
|
|
114
|
-
createdAt: finiteInteger(row.created_at, 'created_at'),
|
|
115
|
-
updatedAt: finiteInteger(row.updated_at, 'updated_at'),
|
|
116
|
-
expiresAt: finiteInteger(row.expires_at, 'expires_at'),
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export function mapRetrievalCycleRow(row: Record<string, unknown>): RetrievalCycleRecord {
|
|
121
|
-
return {
|
|
122
|
-
retrievalSessionId: requiredString(row.retrieval_session_id, 'retrieval_session_id'),
|
|
123
|
-
cycle: finiteInteger(row.cycle, 'cycle'),
|
|
124
|
-
gaps: parseJsonArray(row.gaps_json, 'gaps_json') as RetrievalGapRecord[],
|
|
125
|
-
selectedIds: parseStringArray(row.selected_ids, 'selected_ids'),
|
|
126
|
-
rejectedIds: parseStringArray(row.rejected_ids, 'rejected_ids'),
|
|
127
|
-
tokenCost: finiteInteger(row.token_cost, 'token_cost'),
|
|
128
|
-
remainingTokenBudget: finiteInteger(row.remaining_token_budget, 'remaining_token_budget'),
|
|
129
|
-
quality: parseJsonObject(row.quality_json, 'quality_json'),
|
|
130
|
-
receipt: requiredString(row.receipt, 'receipt'),
|
|
131
|
-
evidenceHash: requiredString(row.evidence_hash, 'evidence_hash'),
|
|
132
|
-
createdAt: finiteInteger(row.created_at, 'created_at'),
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export function serializeRetrievalJson(value: unknown): string {
|
|
137
|
-
return JSON.stringify(canonicalize(value));
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export function isRetrievalSessionState(value: unknown): value is RetrievalSessionState {
|
|
141
|
-
return value === 'open' || value === 'satisfied' || value === 'exhausted' || value === 'expired';
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function canonicalize(value: unknown): unknown {
|
|
145
|
-
if (value === null || typeof value === 'string' || typeof value === 'boolean') return value;
|
|
146
|
-
if (typeof value === 'number') {
|
|
147
|
-
if (!Number.isFinite(value) || Object.is(value, -0)) throw new Error('Retrieval evidence must be lossless JSON');
|
|
148
|
-
return value;
|
|
149
|
-
}
|
|
150
|
-
if (Array.isArray(value)) return value.map(canonicalize);
|
|
151
|
-
if (!value || typeof value !== 'object' || Object.getPrototypeOf(value) !== Object.prototype) {
|
|
152
|
-
throw new Error('Retrieval evidence must be plain JSON');
|
|
153
|
-
}
|
|
154
|
-
const record = value as Record<string, unknown>;
|
|
155
|
-
return Object.fromEntries(Object.keys(record).sort().map(key => [key, canonicalize(record[key])]));
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function parseJson(value: unknown, field: string): unknown {
|
|
159
|
-
if (typeof value !== 'string') throw new Error(`Invalid persisted ${field}: expected JSON text`);
|
|
160
|
-
try { return JSON.parse(value) as unknown; } catch { throw new Error(`Invalid persisted ${field}: malformed JSON`); }
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function parseJsonArray(value: unknown, field: string): unknown[] {
|
|
164
|
-
const parsed = parseJson(value, field);
|
|
165
|
-
if (!Array.isArray(parsed)) throw new Error(`Invalid persisted ${field}: expected array`);
|
|
166
|
-
return parsed;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function parseStringArray(value: unknown, field: string): string[] {
|
|
170
|
-
const parsed = parseJsonArray(value, field);
|
|
171
|
-
if (parsed.some(item => typeof item !== 'string')) throw new Error(`Invalid persisted ${field}: expected strings`);
|
|
172
|
-
return parsed as string[];
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function parseJsonObject(value: unknown, field: string): Record<string, unknown> {
|
|
176
|
-
const parsed = parseJson(value, field);
|
|
177
|
-
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
178
|
-
throw new Error(`Invalid persisted ${field}: expected object`);
|
|
179
|
-
}
|
|
180
|
-
return parsed as Record<string, unknown>;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function requiredString(value: unknown, field: string): string {
|
|
184
|
-
if (typeof value !== 'string' || value.length === 0) throw new Error(`Invalid persisted ${field}`);
|
|
185
|
-
return value;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function optionalString(value: unknown): string | undefined {
|
|
189
|
-
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function finiteInteger(value: unknown, field: string): number {
|
|
193
|
-
const result = Number(value);
|
|
194
|
-
if (!Number.isSafeInteger(result) || result < 0) throw new Error(`Invalid persisted ${field}`);
|
|
195
|
-
return result;
|
|
196
|
-
}
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
export type PersistedSemanticResolutionState = 'provisional' | 'resolved' | 'abstained' | 'unavailable';
|
|
2
|
-
export type PersistedSemanticGeneratedBy = 'deterministic' | 'embedding_router' | 'host_semantic' | 'hybrid';
|
|
3
|
-
|
|
4
|
-
export interface SemanticResolutionRecord {
|
|
5
|
-
frameHash: string;
|
|
6
|
-
projectRoot: string;
|
|
7
|
-
projectId: string;
|
|
8
|
-
hostId: string;
|
|
9
|
-
sessionId: string;
|
|
10
|
-
turnId: string;
|
|
11
|
-
requestId: string;
|
|
12
|
-
sourceHash: string;
|
|
13
|
-
artifactHash: string;
|
|
14
|
-
artifactRevision: number;
|
|
15
|
-
supersedesFrameHash?: string;
|
|
16
|
-
state: PersistedSemanticResolutionState;
|
|
17
|
-
generatedBy: PersistedSemanticGeneratedBy;
|
|
18
|
-
modelId?: string;
|
|
19
|
-
modelRevision?: string;
|
|
20
|
-
routeCatalogVersion: string;
|
|
21
|
-
thresholdVersion: string;
|
|
22
|
-
route: Record<string, unknown>;
|
|
23
|
-
sampling: Record<string, unknown>;
|
|
24
|
-
conflicts: Array<Record<string, unknown>>;
|
|
25
|
-
frame: Record<string, unknown>;
|
|
26
|
-
durationMs: number;
|
|
27
|
-
createdAt: number;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const STATES = new Set<PersistedSemanticResolutionState>([
|
|
31
|
-
'provisional', 'resolved', 'abstained', 'unavailable',
|
|
32
|
-
]);
|
|
33
|
-
const GENERATORS = new Set<PersistedSemanticGeneratedBy>([
|
|
34
|
-
'deterministic', 'embedding_router', 'host_semantic', 'hybrid',
|
|
35
|
-
]);
|
|
36
|
-
|
|
37
|
-
export function assertSemanticResolutionRecord(record: SemanticResolutionRecord): void {
|
|
38
|
-
for (const [field, value] of Object.entries({
|
|
39
|
-
frameHash: record.frameHash,
|
|
40
|
-
projectRoot: record.projectRoot,
|
|
41
|
-
projectId: record.projectId,
|
|
42
|
-
hostId: record.hostId,
|
|
43
|
-
sessionId: record.sessionId,
|
|
44
|
-
turnId: record.turnId,
|
|
45
|
-
requestId: record.requestId,
|
|
46
|
-
sourceHash: record.sourceHash,
|
|
47
|
-
artifactHash: record.artifactHash,
|
|
48
|
-
routeCatalogVersion: record.routeCatalogVersion,
|
|
49
|
-
thresholdVersion: record.thresholdVersion,
|
|
50
|
-
})) {
|
|
51
|
-
if (typeof value !== 'string' || value.length === 0) {
|
|
52
|
-
throw new Error(`SEMANTIC_RESOLUTION_INVALID:${field}`);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
if (!Number.isSafeInteger(record.artifactRevision) || record.artifactRevision < 1) {
|
|
56
|
-
throw new Error('SEMANTIC_RESOLUTION_INVALID:artifactRevision');
|
|
57
|
-
}
|
|
58
|
-
if (!Number.isSafeInteger(record.durationMs) || record.durationMs < 0
|
|
59
|
-
|| !Number.isSafeInteger(record.createdAt) || record.createdAt < 0) {
|
|
60
|
-
throw new Error('SEMANTIC_RESOLUTION_INVALID:timing');
|
|
61
|
-
}
|
|
62
|
-
if (!STATES.has(record.state) || !GENERATORS.has(record.generatedBy)) {
|
|
63
|
-
throw new Error('SEMANTIC_RESOLUTION_INVALID:enum');
|
|
64
|
-
}
|
|
65
|
-
if (record.frame.frameHash !== record.frameHash) {
|
|
66
|
-
throw new Error('SEMANTIC_RESOLUTION_FRAME_HASH_MISMATCH');
|
|
67
|
-
}
|
|
68
|
-
stableSemanticResolutionJson(record.route);
|
|
69
|
-
stableSemanticResolutionJson(record.sampling);
|
|
70
|
-
stableSemanticResolutionJson(record.conflicts);
|
|
71
|
-
stableSemanticResolutionJson(record.frame);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function stableSemanticResolutionJson(value: unknown): string {
|
|
75
|
-
return JSON.stringify(canonicalize(value));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function mapSemanticResolutionRow(row: Record<string, unknown>): SemanticResolutionRecord {
|
|
79
|
-
const state = requiredString(row.state, 'state') as PersistedSemanticResolutionState;
|
|
80
|
-
const generatedBy = requiredString(row.generated_by, 'generated_by') as PersistedSemanticGeneratedBy;
|
|
81
|
-
if (!STATES.has(state) || !GENERATORS.has(generatedBy)) {
|
|
82
|
-
throw new Error('SEMANTIC_RESOLUTION_ROW_INVALID:enum');
|
|
83
|
-
}
|
|
84
|
-
const result: SemanticResolutionRecord = {
|
|
85
|
-
frameHash: requiredString(row.frame_hash, 'frame_hash'),
|
|
86
|
-
projectRoot: requiredString(row.project_root, 'project_root'),
|
|
87
|
-
projectId: requiredString(row.project_id, 'project_id'),
|
|
88
|
-
hostId: requiredString(row.host_id, 'host_id'),
|
|
89
|
-
sessionId: requiredString(row.session_id, 'session_id'),
|
|
90
|
-
turnId: requiredString(row.turn_id, 'turn_id'),
|
|
91
|
-
requestId: requiredString(row.request_id, 'request_id'),
|
|
92
|
-
sourceHash: requiredString(row.source_hash, 'source_hash'),
|
|
93
|
-
artifactHash: requiredString(row.artifact_hash, 'artifact_hash'),
|
|
94
|
-
artifactRevision: requiredPositiveInteger(row.artifact_revision, 'artifact_revision'),
|
|
95
|
-
...(optionalString(row.supersedes_frame_hash)
|
|
96
|
-
? { supersedesFrameHash: optionalString(row.supersedes_frame_hash) }
|
|
97
|
-
: {}),
|
|
98
|
-
state,
|
|
99
|
-
generatedBy,
|
|
100
|
-
...(optionalString(row.model_id) ? { modelId: optionalString(row.model_id) } : {}),
|
|
101
|
-
...(optionalString(row.model_revision) ? { modelRevision: optionalString(row.model_revision) } : {}),
|
|
102
|
-
routeCatalogVersion: requiredString(row.route_catalog_version, 'route_catalog_version'),
|
|
103
|
-
thresholdVersion: requiredString(row.threshold_version, 'threshold_version'),
|
|
104
|
-
route: parseObject(row.route_json, 'route_json'),
|
|
105
|
-
sampling: parseObject(row.sampling_json, 'sampling_json'),
|
|
106
|
-
conflicts: parseObjectArray(row.conflicts_json, 'conflicts_json'),
|
|
107
|
-
frame: parseObject(row.frame_json, 'frame_json'),
|
|
108
|
-
durationMs: requiredNonNegativeInteger(row.duration_ms, 'duration_ms'),
|
|
109
|
-
createdAt: requiredNonNegativeInteger(row.created_at, 'created_at'),
|
|
110
|
-
};
|
|
111
|
-
assertSemanticResolutionRecord(result);
|
|
112
|
-
return result;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function canonicalize(value: unknown): unknown {
|
|
116
|
-
if (value === undefined) return undefined;
|
|
117
|
-
if (value === null || typeof value === 'string' || typeof value === 'boolean') return value;
|
|
118
|
-
if (typeof value === 'number') {
|
|
119
|
-
if (!Number.isFinite(value) || Object.is(value, -0)) throw new Error('SEMANTIC_RESOLUTION_JSON_INVALID');
|
|
120
|
-
return value;
|
|
121
|
-
}
|
|
122
|
-
if (Array.isArray(value)) return value.map(item => {
|
|
123
|
-
if (item === undefined) throw new Error('SEMANTIC_RESOLUTION_JSON_INVALID');
|
|
124
|
-
return canonicalize(item);
|
|
125
|
-
});
|
|
126
|
-
if (!value || typeof value !== 'object' || Object.getPrototypeOf(value) !== Object.prototype) {
|
|
127
|
-
throw new Error('SEMANTIC_RESOLUTION_JSON_INVALID');
|
|
128
|
-
}
|
|
129
|
-
const record = value as Record<string, unknown>;
|
|
130
|
-
return Object.fromEntries(Object.keys(record).sort()
|
|
131
|
-
.filter(key => record[key] !== undefined)
|
|
132
|
-
.map(key => [key, canonicalize(record[key])]));
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function parseObject(value: unknown, field: string): Record<string, unknown> {
|
|
136
|
-
const parsed = parseJson(value, field);
|
|
137
|
-
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
138
|
-
throw new Error(`SEMANTIC_RESOLUTION_ROW_INVALID:${field}`);
|
|
139
|
-
}
|
|
140
|
-
return parsed as Record<string, unknown>;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function parseObjectArray(value: unknown, field: string): Array<Record<string, unknown>> {
|
|
144
|
-
const parsed = parseJson(value, field);
|
|
145
|
-
if (!Array.isArray(parsed) || parsed.some(item => !item || typeof item !== 'object' || Array.isArray(item))) {
|
|
146
|
-
throw new Error(`SEMANTIC_RESOLUTION_ROW_INVALID:${field}`);
|
|
147
|
-
}
|
|
148
|
-
return parsed as Array<Record<string, unknown>>;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function parseJson(value: unknown, field: string): unknown {
|
|
152
|
-
if (typeof value !== 'string') throw new Error(`SEMANTIC_RESOLUTION_ROW_INVALID:${field}`);
|
|
153
|
-
try { return JSON.parse(value) as unknown; } catch {
|
|
154
|
-
throw new Error(`SEMANTIC_RESOLUTION_ROW_INVALID:${field}`);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function requiredString(value: unknown, field: string): string {
|
|
159
|
-
if (typeof value !== 'string' || value.length === 0) {
|
|
160
|
-
throw new Error(`SEMANTIC_RESOLUTION_ROW_INVALID:${field}`);
|
|
161
|
-
}
|
|
162
|
-
return value;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function optionalString(value: unknown): string | undefined {
|
|
166
|
-
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function requiredPositiveInteger(value: unknown, field: string): number {
|
|
170
|
-
const result = requiredNonNegativeInteger(value, field);
|
|
171
|
-
if (result < 1) throw new Error(`SEMANTIC_RESOLUTION_ROW_INVALID:${field}`);
|
|
172
|
-
return result;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function requiredNonNegativeInteger(value: unknown, field: string): number {
|
|
176
|
-
const result = Number(value);
|
|
177
|
-
if (!Number.isSafeInteger(result) || result < 0) {
|
|
178
|
-
throw new Error(`SEMANTIC_RESOLUTION_ROW_INVALID:${field}`);
|
|
179
|
-
}
|
|
180
|
-
return result;
|
|
181
|
-
}
|
package/src/task-runtime.ts
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
|
|
3
|
-
export interface TaskRuntimeIdentityRecord {
|
|
4
|
-
projectRoot: string;
|
|
5
|
-
projectId: string;
|
|
6
|
-
hostId: string;
|
|
7
|
-
sessionId: string;
|
|
8
|
-
turnId: string;
|
|
9
|
-
requestId: string;
|
|
10
|
-
executionId?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface TaskRuntimeEventRecord {
|
|
14
|
-
schemaVersion: 'task-runtime-event.v1';
|
|
15
|
-
producer: string;
|
|
16
|
-
producerVersion: string;
|
|
17
|
-
eventId: string;
|
|
18
|
-
sequence: number;
|
|
19
|
-
identity: TaskRuntimeIdentityRecord;
|
|
20
|
-
taskSpecHash: string;
|
|
21
|
-
kind: string;
|
|
22
|
-
payload: Record<string, unknown>;
|
|
23
|
-
createdAt: number;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export type TaskRuntimeChannelRequirement = 'required' | 'optional' | 'not_required';
|
|
27
|
-
export type TaskRuntimeChannelStatus =
|
|
28
|
-
| 'satisfied' | 'intentional_abstention' | 'empty_valid' | 'degraded' | 'failed';
|
|
29
|
-
export type TaskRuntimeObligationState = 'pending' | 'satisfied' | 'degraded' | 'failed';
|
|
30
|
-
|
|
31
|
-
export interface TaskRuntimeChannelRecord {
|
|
32
|
-
requirement: TaskRuntimeChannelRequirement;
|
|
33
|
-
status: TaskRuntimeChannelStatus;
|
|
34
|
-
reasonCode: string;
|
|
35
|
-
evidenceIds: string[];
|
|
36
|
-
material: boolean;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface TaskRuntimeObligationRecord {
|
|
40
|
-
requirement: TaskRuntimeChannelRequirement;
|
|
41
|
-
state: TaskRuntimeObligationState;
|
|
42
|
-
receiptId?: string;
|
|
43
|
-
reasonCode?: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface TaskRuntimeSnapshotRecord {
|
|
47
|
-
schemaVersion: 'task-runtime-snapshot.v1';
|
|
48
|
-
producer: string;
|
|
49
|
-
producerVersion: string;
|
|
50
|
-
identity: TaskRuntimeIdentityRecord;
|
|
51
|
-
taskSpecHash: string;
|
|
52
|
-
lastEventSequence: number;
|
|
53
|
-
artifacts: {
|
|
54
|
-
requiredIdentityIds: string[];
|
|
55
|
-
resolvedIdentityIds: string[];
|
|
56
|
-
ambiguousIdentityIds: string[];
|
|
57
|
-
};
|
|
58
|
-
channels: Partial<Record<'code' | 'memory' | 'knowledge', TaskRuntimeChannelRecord>>;
|
|
59
|
-
contextReceiptId?: string;
|
|
60
|
-
action: TaskRuntimeObligationRecord;
|
|
61
|
-
verification: TaskRuntimeObligationRecord;
|
|
62
|
-
memory: TaskRuntimeObligationRecord;
|
|
63
|
-
durableWorkIds: string[];
|
|
64
|
-
terminal: 'running' | 'retry_pending' | 'completed' | 'completed_with_degradation' | 'failed';
|
|
65
|
-
runtimeHealth: 'healthy' | 'degraded' | 'unavailable';
|
|
66
|
-
snapshotHash: string;
|
|
67
|
-
updatedAt: number;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function stableTaskRuntimeJson(value: unknown): string {
|
|
71
|
-
return JSON.stringify(canonicalize(value));
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function stableTaskRuntimeHash(value: unknown): string {
|
|
75
|
-
return createHash('sha256').update(stableTaskRuntimeJson(value)).digest('hex');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function mapTaskRuntimeEventRow(row: Record<string, unknown>): TaskRuntimeEventRecord {
|
|
79
|
-
return {
|
|
80
|
-
schemaVersion: requiredLiteral(row.schema_version, 'task-runtime-event.v1'),
|
|
81
|
-
producer: requiredString(row.producer, 'producer'),
|
|
82
|
-
producerVersion: requiredString(row.producer_version, 'producer_version'),
|
|
83
|
-
eventId: requiredString(row.event_id, 'event_id'),
|
|
84
|
-
sequence: requiredPositiveInteger(row.sequence, 'sequence'),
|
|
85
|
-
identity: mapIdentity(row),
|
|
86
|
-
taskSpecHash: requiredString(row.task_spec_hash, 'task_spec_hash'),
|
|
87
|
-
kind: requiredString(row.kind, 'kind'),
|
|
88
|
-
payload: parseObject(row.payload_json, 'payload_json'),
|
|
89
|
-
createdAt: requiredNonNegativeInteger(row.created_at, 'created_at'),
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export function mapTaskRuntimeSnapshotRow(row: Record<string, unknown>): TaskRuntimeSnapshotRecord {
|
|
94
|
-
const snapshot = parseObject(row.snapshot_json, 'snapshot_json') as unknown as TaskRuntimeSnapshotRecord;
|
|
95
|
-
if (snapshot.schemaVersion !== 'task-runtime-snapshot.v1') {
|
|
96
|
-
throw new Error(`TASK_RUNTIME_SCHEMA_UNSUPPORTED:${String(snapshot.schemaVersion)}`);
|
|
97
|
-
}
|
|
98
|
-
if (snapshot.snapshotHash !== requiredString(row.snapshot_hash, 'snapshot_hash')) {
|
|
99
|
-
throw new Error('TASK_RUNTIME_SNAPSHOT_HASH_MISMATCH');
|
|
100
|
-
}
|
|
101
|
-
const { snapshotHash: _snapshotHash, ...body } = snapshot;
|
|
102
|
-
if (stableTaskRuntimeHash(body) !== snapshot.snapshotHash) {
|
|
103
|
-
throw new Error('TASK_RUNTIME_SNAPSHOT_HASH_MISMATCH');
|
|
104
|
-
}
|
|
105
|
-
return snapshot;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function mapIdentity(row: Record<string, unknown>): TaskRuntimeIdentityRecord {
|
|
109
|
-
return {
|
|
110
|
-
projectRoot: requiredString(row.project_root, 'project_root'),
|
|
111
|
-
projectId: requiredString(row.project_id, 'project_id'),
|
|
112
|
-
hostId: requiredString(row.host_id, 'host_id'),
|
|
113
|
-
sessionId: requiredString(row.session_id, 'session_id'),
|
|
114
|
-
turnId: requiredString(row.turn_id, 'turn_id'),
|
|
115
|
-
requestId: requiredString(row.request_id, 'request_id'),
|
|
116
|
-
...(optionalString(row.execution_id) ? { executionId: optionalString(row.execution_id) } : {}),
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function requiredLiteral<T extends string>(value: unknown, literal: T): T {
|
|
121
|
-
if (value !== literal) throw new Error(`TASK_RUNTIME_SCHEMA_UNSUPPORTED:${String(value)}`);
|
|
122
|
-
return literal;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function requiredString(value: unknown, field: string): string {
|
|
126
|
-
if (typeof value !== 'string' || !value.trim()) throw new Error(`TASK_RUNTIME_ROW_INVALID:${field}`);
|
|
127
|
-
return value;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function optionalString(value: unknown): string | undefined {
|
|
131
|
-
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function requiredPositiveInteger(value: unknown, field: string): number {
|
|
135
|
-
const result = Number(value);
|
|
136
|
-
if (!Number.isSafeInteger(result) || result < 1) throw new Error(`TASK_RUNTIME_ROW_INVALID:${field}`);
|
|
137
|
-
return result;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function requiredNonNegativeInteger(value: unknown, field: string): number {
|
|
141
|
-
const result = Number(value);
|
|
142
|
-
if (!Number.isSafeInteger(result) || result < 0) throw new Error(`TASK_RUNTIME_ROW_INVALID:${field}`);
|
|
143
|
-
return result;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function parseObject(value: unknown, field: string): Record<string, unknown> {
|
|
147
|
-
if (typeof value !== 'string') throw new Error(`TASK_RUNTIME_ROW_INVALID:${field}`);
|
|
148
|
-
try {
|
|
149
|
-
const parsed = JSON.parse(value) as unknown;
|
|
150
|
-
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('not object');
|
|
151
|
-
return parsed as Record<string, unknown>;
|
|
152
|
-
} catch {
|
|
153
|
-
throw new Error(`TASK_RUNTIME_ROW_INVALID:${field}`);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function canonicalize(value: unknown): unknown {
|
|
158
|
-
if (value === null || typeof value === 'string' || typeof value === 'boolean') return value;
|
|
159
|
-
if (typeof value === 'number') {
|
|
160
|
-
if (!Number.isFinite(value) || Object.is(value, -0)) throw new Error('TASK_RUNTIME_JSON_INVALID');
|
|
161
|
-
return value;
|
|
162
|
-
}
|
|
163
|
-
if (Array.isArray(value)) return value.map(canonicalize);
|
|
164
|
-
if (!value || typeof value !== 'object' || Object.getPrototypeOf(value) !== Object.prototype) {
|
|
165
|
-
throw new Error('TASK_RUNTIME_JSON_INVALID');
|
|
166
|
-
}
|
|
167
|
-
return Object.fromEntries(Object.keys(value as Record<string, unknown>).sort()
|
|
168
|
-
.map(key => [key, canonicalize((value as Record<string, unknown>)[key])]));
|
|
169
|
-
}
|