@devflow-tools/database 0.17.2 → 0.17.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/CHANGELOG.md +19 -0
- package/__tests__/database.retrieval-ledger.test.ts +15 -0
- package/__tests__/database.task-runtime.test.ts +73 -0
- package/__tests__/database.work-queue.test.ts +34 -0
- package/dist/database.d.ts +34 -0
- package/dist/database.js +294 -12
- package/dist/index.d.ts +3 -1
- package/dist/index.js +6 -1
- package/dist/retrieval-ledger.d.ts +8 -1
- package/dist/task-runtime.d.ts +64 -0
- package/dist/task-runtime.js +106 -0
- package/dist/work-queue.d.ts +18 -2
- package/package.json +1 -1
- package/src/database.ts +298 -9
- package/src/index.ts +17 -0
- package/src/retrieval-ledger.ts +9 -2
- package/src/task-runtime.ts +169 -0
- package/src/work-queue.ts +20 -1
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stableTaskRuntimeJson = stableTaskRuntimeJson;
|
|
4
|
+
exports.stableTaskRuntimeHash = stableTaskRuntimeHash;
|
|
5
|
+
exports.mapTaskRuntimeEventRow = mapTaskRuntimeEventRow;
|
|
6
|
+
exports.mapTaskRuntimeSnapshotRow = mapTaskRuntimeSnapshotRow;
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
function stableTaskRuntimeJson(value) {
|
|
9
|
+
return JSON.stringify(canonicalize(value));
|
|
10
|
+
}
|
|
11
|
+
function stableTaskRuntimeHash(value) {
|
|
12
|
+
return (0, node_crypto_1.createHash)('sha256').update(stableTaskRuntimeJson(value)).digest('hex');
|
|
13
|
+
}
|
|
14
|
+
function mapTaskRuntimeEventRow(row) {
|
|
15
|
+
return {
|
|
16
|
+
schemaVersion: requiredLiteral(row.schema_version, 'task-runtime-event.v1'),
|
|
17
|
+
producer: requiredString(row.producer, 'producer'),
|
|
18
|
+
producerVersion: requiredString(row.producer_version, 'producer_version'),
|
|
19
|
+
eventId: requiredString(row.event_id, 'event_id'),
|
|
20
|
+
sequence: requiredPositiveInteger(row.sequence, 'sequence'),
|
|
21
|
+
identity: mapIdentity(row),
|
|
22
|
+
taskSpecHash: requiredString(row.task_spec_hash, 'task_spec_hash'),
|
|
23
|
+
kind: requiredString(row.kind, 'kind'),
|
|
24
|
+
payload: parseObject(row.payload_json, 'payload_json'),
|
|
25
|
+
createdAt: requiredNonNegativeInteger(row.created_at, 'created_at'),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function mapTaskRuntimeSnapshotRow(row) {
|
|
29
|
+
const snapshot = parseObject(row.snapshot_json, 'snapshot_json');
|
|
30
|
+
if (snapshot.schemaVersion !== 'task-runtime-snapshot.v1') {
|
|
31
|
+
throw new Error(`TASK_RUNTIME_SCHEMA_UNSUPPORTED:${String(snapshot.schemaVersion)}`);
|
|
32
|
+
}
|
|
33
|
+
if (snapshot.snapshotHash !== requiredString(row.snapshot_hash, 'snapshot_hash')) {
|
|
34
|
+
throw new Error('TASK_RUNTIME_SNAPSHOT_HASH_MISMATCH');
|
|
35
|
+
}
|
|
36
|
+
const { snapshotHash: _snapshotHash, ...body } = snapshot;
|
|
37
|
+
if (stableTaskRuntimeHash(body) !== snapshot.snapshotHash) {
|
|
38
|
+
throw new Error('TASK_RUNTIME_SNAPSHOT_HASH_MISMATCH');
|
|
39
|
+
}
|
|
40
|
+
return snapshot;
|
|
41
|
+
}
|
|
42
|
+
function mapIdentity(row) {
|
|
43
|
+
return {
|
|
44
|
+
projectRoot: requiredString(row.project_root, 'project_root'),
|
|
45
|
+
projectId: requiredString(row.project_id, 'project_id'),
|
|
46
|
+
hostId: requiredString(row.host_id, 'host_id'),
|
|
47
|
+
sessionId: requiredString(row.session_id, 'session_id'),
|
|
48
|
+
turnId: requiredString(row.turn_id, 'turn_id'),
|
|
49
|
+
requestId: requiredString(row.request_id, 'request_id'),
|
|
50
|
+
...(optionalString(row.execution_id) ? { executionId: optionalString(row.execution_id) } : {}),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function requiredLiteral(value, literal) {
|
|
54
|
+
if (value !== literal)
|
|
55
|
+
throw new Error(`TASK_RUNTIME_SCHEMA_UNSUPPORTED:${String(value)}`);
|
|
56
|
+
return literal;
|
|
57
|
+
}
|
|
58
|
+
function requiredString(value, field) {
|
|
59
|
+
if (typeof value !== 'string' || !value.trim())
|
|
60
|
+
throw new Error(`TASK_RUNTIME_ROW_INVALID:${field}`);
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
function optionalString(value) {
|
|
64
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
65
|
+
}
|
|
66
|
+
function requiredPositiveInteger(value, field) {
|
|
67
|
+
const result = Number(value);
|
|
68
|
+
if (!Number.isSafeInteger(result) || result < 1)
|
|
69
|
+
throw new Error(`TASK_RUNTIME_ROW_INVALID:${field}`);
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
function requiredNonNegativeInteger(value, field) {
|
|
73
|
+
const result = Number(value);
|
|
74
|
+
if (!Number.isSafeInteger(result) || result < 0)
|
|
75
|
+
throw new Error(`TASK_RUNTIME_ROW_INVALID:${field}`);
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
function parseObject(value, field) {
|
|
79
|
+
if (typeof value !== 'string')
|
|
80
|
+
throw new Error(`TASK_RUNTIME_ROW_INVALID:${field}`);
|
|
81
|
+
try {
|
|
82
|
+
const parsed = JSON.parse(value);
|
|
83
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
84
|
+
throw new Error('not object');
|
|
85
|
+
return parsed;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
throw new Error(`TASK_RUNTIME_ROW_INVALID:${field}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function canonicalize(value) {
|
|
92
|
+
if (value === null || typeof value === 'string' || typeof value === 'boolean')
|
|
93
|
+
return value;
|
|
94
|
+
if (typeof value === 'number') {
|
|
95
|
+
if (!Number.isFinite(value) || Object.is(value, -0))
|
|
96
|
+
throw new Error('TASK_RUNTIME_JSON_INVALID');
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
if (Array.isArray(value))
|
|
100
|
+
return value.map(canonicalize);
|
|
101
|
+
if (!value || typeof value !== 'object' || Object.getPrototypeOf(value) !== Object.prototype) {
|
|
102
|
+
throw new Error('TASK_RUNTIME_JSON_INVALID');
|
|
103
|
+
}
|
|
104
|
+
return Object.fromEntries(Object.keys(value).sort()
|
|
105
|
+
.map(key => [key, canonicalize(value[key])]));
|
|
106
|
+
}
|
package/dist/work-queue.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type WorkKind = 'memory.explicit_commit' | 'memory.explicit_enrichment' | 'memory.turn_capture' | 'memory.turn_distill' | 'memory.vector_backfill' | 'context.prefetch' | 'verification.policy' | 'learning.analyze_session' | 'workflow.worker_start' | 'workflow.worker_monitor' | 'workflow.worker_verify' | 'workflow.worker_merge' | 'workflow.worker_cleanup' | 'session.bootstrap' | 'session.finalize' | 'telemetry.reconcile';
|
|
2
|
-
export type WorkState = 'pending' | 'leased' | 'completed' | 'failed' | 'dead_letter';
|
|
1
|
+
export type WorkKind = 'memory.explicit_commit' | 'memory.explicit_enrichment' | 'memory.turn_capture' | 'memory.turn_distill' | 'memory.vector_backfill' | 'knowledge.ingest' | 'knowledge.index_refresh' | 'context.prefetch' | 'verification.policy' | 'learning.analyze_session' | 'workflow.worker_start' | 'workflow.worker_monitor' | 'workflow.worker_verify' | 'workflow.worker_merge' | 'workflow.worker_cleanup' | 'session.bootstrap' | 'session.finalize' | 'telemetry.reconcile';
|
|
2
|
+
export type WorkState = 'pending' | 'leased' | 'completed' | 'failed' | 'dead_letter' | 'cancelled';
|
|
3
3
|
export interface WorkError {
|
|
4
4
|
category: string;
|
|
5
5
|
message: string;
|
|
@@ -11,6 +11,8 @@ export interface WorkItemRecord {
|
|
|
11
11
|
projectRoot: string;
|
|
12
12
|
sessionId?: string;
|
|
13
13
|
turnId?: string;
|
|
14
|
+
sourceHash?: string;
|
|
15
|
+
taskSpecHash?: string;
|
|
14
16
|
payload: unknown;
|
|
15
17
|
state: WorkState;
|
|
16
18
|
attempts: number;
|
|
@@ -23,6 +25,10 @@ export interface WorkItemRecord {
|
|
|
23
25
|
createdAt: number;
|
|
24
26
|
updatedAt: number;
|
|
25
27
|
completedAt?: number;
|
|
28
|
+
cancelledAt?: number;
|
|
29
|
+
cancellationReason?: string;
|
|
30
|
+
replayOfId?: string;
|
|
31
|
+
replayCount: number;
|
|
26
32
|
}
|
|
27
33
|
export interface EnqueueWorkInput {
|
|
28
34
|
idempotencyKey: string;
|
|
@@ -30,10 +36,20 @@ export interface EnqueueWorkInput {
|
|
|
30
36
|
projectRoot: string;
|
|
31
37
|
sessionId?: string;
|
|
32
38
|
turnId?: string;
|
|
39
|
+
sourceHash?: string;
|
|
40
|
+
taskSpecHash?: string;
|
|
33
41
|
payload: unknown;
|
|
34
42
|
maxAttempts?: number;
|
|
35
43
|
nextAttemptAt?: number;
|
|
36
44
|
}
|
|
45
|
+
export interface ProjectMutationLeaseRecord {
|
|
46
|
+
projectRoot: string;
|
|
47
|
+
mutationKind: string;
|
|
48
|
+
owner: string;
|
|
49
|
+
leaseExpiresAt: number;
|
|
50
|
+
sourceHash?: string;
|
|
51
|
+
updatedAt: number;
|
|
52
|
+
}
|
|
37
53
|
export interface LeaseWorkInput {
|
|
38
54
|
projectRoot: string;
|
|
39
55
|
owner: string;
|
package/package.json
CHANGED
package/src/database.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
WorkItemRecord,
|
|
13
13
|
WorkQueueHealth,
|
|
14
14
|
WorkState,
|
|
15
|
+
ProjectMutationLeaseRecord,
|
|
15
16
|
} from './work-queue';
|
|
16
17
|
import {
|
|
17
18
|
mapSessionObligationRow,
|
|
@@ -84,6 +85,13 @@ import {
|
|
|
84
85
|
type ToolNameResolutionRecord,
|
|
85
86
|
type TranscriptCheckpointRecord,
|
|
86
87
|
} from './task-semantic-control';
|
|
88
|
+
import {
|
|
89
|
+
mapTaskRuntimeEventRow,
|
|
90
|
+
mapTaskRuntimeSnapshotRow,
|
|
91
|
+
stableTaskRuntimeJson,
|
|
92
|
+
type TaskRuntimeEventRecord,
|
|
93
|
+
type TaskRuntimeSnapshotRecord,
|
|
94
|
+
} from './task-runtime';
|
|
87
95
|
|
|
88
96
|
export interface BenchmarkReportRecord {
|
|
89
97
|
runId: string;
|
|
@@ -612,6 +620,9 @@ export class DevFlowDatabase {
|
|
|
612
620
|
source_id TEXT NOT NULL, stage TEXT NOT NULL, rank INTEGER, raw_score REAL,
|
|
613
621
|
normalized_score REAL, final_score REAL, applicability TEXT NOT NULL DEFAULT '[]',
|
|
614
622
|
reason TEXT, tool_evidence TEXT NOT NULL DEFAULT '[]', verification_receipt TEXT,
|
|
623
|
+
schema_version TEXT NOT NULL DEFAULT 'retrieval-ledger-event.v1', task_spec_hash TEXT,
|
|
624
|
+
actor TEXT, source_version TEXT, source_content_hash TEXT,
|
|
625
|
+
evidence_ids TEXT NOT NULL DEFAULT '[]', reason_code TEXT,
|
|
615
626
|
payload TEXT NOT NULL DEFAULT '{}', created_at INTEGER NOT NULL
|
|
616
627
|
);
|
|
617
628
|
CREATE INDEX IF NOT EXISTS idx_retrieval_ledger_identity ON devflow_retrieval_ledger(
|
|
@@ -716,6 +727,40 @@ export class DevFlowDatabase {
|
|
|
716
727
|
CREATE INDEX IF NOT EXISTS idx_terminal_transition_turn
|
|
717
728
|
ON devflow_terminal_transitions(project_root, session_id, turn_id, sequence);
|
|
718
729
|
|
|
730
|
+
CREATE TABLE IF NOT EXISTS devflow_task_runtime_events (
|
|
731
|
+
event_id TEXT PRIMARY KEY,
|
|
732
|
+
schema_version TEXT NOT NULL,
|
|
733
|
+
producer TEXT NOT NULL,
|
|
734
|
+
producer_version TEXT NOT NULL,
|
|
735
|
+
project_root TEXT NOT NULL,
|
|
736
|
+
project_id TEXT NOT NULL,
|
|
737
|
+
host_id TEXT NOT NULL,
|
|
738
|
+
session_id TEXT NOT NULL,
|
|
739
|
+
turn_id TEXT NOT NULL,
|
|
740
|
+
request_id TEXT NOT NULL,
|
|
741
|
+
execution_id TEXT,
|
|
742
|
+
task_spec_hash TEXT NOT NULL,
|
|
743
|
+
sequence INTEGER NOT NULL CHECK(sequence > 0),
|
|
744
|
+
kind TEXT NOT NULL,
|
|
745
|
+
payload_json TEXT NOT NULL DEFAULT '{}',
|
|
746
|
+
created_at INTEGER NOT NULL,
|
|
747
|
+
UNIQUE(project_root, session_id, turn_id, sequence)
|
|
748
|
+
);
|
|
749
|
+
CREATE INDEX IF NOT EXISTS idx_task_runtime_event_identity
|
|
750
|
+
ON devflow_task_runtime_events(project_root, session_id, turn_id, sequence);
|
|
751
|
+
|
|
752
|
+
CREATE TABLE IF NOT EXISTS devflow_task_runtime_snapshots (
|
|
753
|
+
project_root TEXT NOT NULL,
|
|
754
|
+
session_id TEXT NOT NULL,
|
|
755
|
+
turn_id TEXT NOT NULL,
|
|
756
|
+
last_event_sequence INTEGER NOT NULL,
|
|
757
|
+
schema_version TEXT NOT NULL,
|
|
758
|
+
snapshot_json TEXT NOT NULL,
|
|
759
|
+
snapshot_hash TEXT NOT NULL,
|
|
760
|
+
updated_at INTEGER NOT NULL,
|
|
761
|
+
PRIMARY KEY(project_root, session_id, turn_id)
|
|
762
|
+
);
|
|
763
|
+
|
|
719
764
|
CREATE TABLE IF NOT EXISTS devflow_transcript_checkpoints (
|
|
720
765
|
id TEXT PRIMARY KEY,
|
|
721
766
|
project_root TEXT NOT NULL,
|
|
@@ -869,9 +914,11 @@ export class DevFlowDatabase {
|
|
|
869
914
|
project_root TEXT NOT NULL,
|
|
870
915
|
session_id TEXT,
|
|
871
916
|
turn_id TEXT,
|
|
917
|
+
source_hash TEXT,
|
|
918
|
+
task_spec_hash TEXT,
|
|
872
919
|
payload TEXT NOT NULL,
|
|
873
920
|
state TEXT NOT NULL DEFAULT 'pending'
|
|
874
|
-
CHECK(state IN ('pending', 'leased', 'completed', 'failed', 'dead_letter')),
|
|
921
|
+
CHECK(state IN ('pending', 'leased', 'completed', 'failed', 'dead_letter', 'cancelled')),
|
|
875
922
|
attempts INTEGER NOT NULL DEFAULT 0 CHECK(attempts >= 0),
|
|
876
923
|
max_attempts INTEGER NOT NULL DEFAULT 5 CHECK(max_attempts > 0),
|
|
877
924
|
lease_owner TEXT,
|
|
@@ -881,7 +928,11 @@ export class DevFlowDatabase {
|
|
|
881
928
|
error_message TEXT,
|
|
882
929
|
created_at INTEGER NOT NULL,
|
|
883
930
|
updated_at INTEGER NOT NULL,
|
|
884
|
-
completed_at INTEGER
|
|
931
|
+
completed_at INTEGER,
|
|
932
|
+
cancelled_at INTEGER,
|
|
933
|
+
cancellation_reason TEXT,
|
|
934
|
+
replay_of_id TEXT,
|
|
935
|
+
replay_count INTEGER NOT NULL DEFAULT 0
|
|
885
936
|
);
|
|
886
937
|
|
|
887
938
|
CREATE INDEX IF NOT EXISTS idx_work_items_ready
|
|
@@ -891,6 +942,16 @@ export class DevFlowDatabase {
|
|
|
891
942
|
CREATE INDEX IF NOT EXISTS idx_work_items_completed
|
|
892
943
|
ON devflow_work_items(project_root, completed_at DESC);
|
|
893
944
|
|
|
945
|
+
CREATE TABLE IF NOT EXISTS devflow_project_mutation_leases (
|
|
946
|
+
project_root TEXT NOT NULL,
|
|
947
|
+
mutation_kind TEXT NOT NULL,
|
|
948
|
+
owner TEXT NOT NULL,
|
|
949
|
+
lease_expires_at INTEGER NOT NULL,
|
|
950
|
+
source_hash TEXT,
|
|
951
|
+
updated_at INTEGER NOT NULL,
|
|
952
|
+
PRIMARY KEY(project_root, mutation_kind)
|
|
953
|
+
);
|
|
954
|
+
|
|
894
955
|
CREATE TABLE IF NOT EXISTS devflow_session_closures (
|
|
895
956
|
session_id TEXT NOT NULL,
|
|
896
957
|
project_root TEXT NOT NULL,
|
|
@@ -1162,6 +1223,19 @@ export class DevFlowDatabase {
|
|
|
1162
1223
|
try { this.db.exec('ALTER TABLE devflow_context_receipts ADD COLUMN action_satisfied_at INTEGER'); } catch {}
|
|
1163
1224
|
try { this.db.exec('ALTER TABLE devflow_context_receipts ADD COLUMN action_degradation_json TEXT'); } catch {}
|
|
1164
1225
|
try { this.db.exec('ALTER TABLE devflow_context_receipts ADD COLUMN request_id TEXT'); } catch {}
|
|
1226
|
+
try { this.db.exec("ALTER TABLE devflow_retrieval_ledger ADD COLUMN schema_version TEXT NOT NULL DEFAULT 'retrieval-ledger-event.v1'"); } catch {}
|
|
1227
|
+
try { this.db.exec('ALTER TABLE devflow_retrieval_ledger ADD COLUMN task_spec_hash TEXT'); } catch {}
|
|
1228
|
+
try { this.db.exec('ALTER TABLE devflow_retrieval_ledger ADD COLUMN actor TEXT'); } catch {}
|
|
1229
|
+
try { this.db.exec('ALTER TABLE devflow_retrieval_ledger ADD COLUMN source_version TEXT'); } catch {}
|
|
1230
|
+
try { this.db.exec('ALTER TABLE devflow_retrieval_ledger ADD COLUMN source_content_hash TEXT'); } catch {}
|
|
1231
|
+
try { this.db.exec("ALTER TABLE devflow_retrieval_ledger ADD COLUMN evidence_ids TEXT NOT NULL DEFAULT '[]'"); } catch {}
|
|
1232
|
+
try { this.db.exec('ALTER TABLE devflow_retrieval_ledger ADD COLUMN reason_code TEXT'); } catch {}
|
|
1233
|
+
try { this.db.exec('ALTER TABLE devflow_work_items ADD COLUMN source_hash TEXT'); } catch {}
|
|
1234
|
+
try { this.db.exec('ALTER TABLE devflow_work_items ADD COLUMN task_spec_hash TEXT'); } catch {}
|
|
1235
|
+
try { this.db.exec('ALTER TABLE devflow_work_items ADD COLUMN cancelled_at INTEGER'); } catch {}
|
|
1236
|
+
try { this.db.exec('ALTER TABLE devflow_work_items ADD COLUMN cancellation_reason TEXT'); } catch {}
|
|
1237
|
+
try { this.db.exec('ALTER TABLE devflow_work_items ADD COLUMN replay_of_id TEXT'); } catch {}
|
|
1238
|
+
try { this.db.exec('ALTER TABLE devflow_work_items ADD COLUMN replay_count INTEGER NOT NULL DEFAULT 0'); } catch {}
|
|
1165
1239
|
this.db.exec('UPDATE devflow_rules SET updated_at = created_at WHERE updated_at IS NULL');
|
|
1166
1240
|
this.db.exec('CREATE INDEX IF NOT EXISTS idx_devflow_rules_gate ON devflow_rules(gate, enabled)');
|
|
1167
1241
|
|
|
@@ -3010,7 +3084,8 @@ export class DevFlowDatabase {
|
|
|
3010
3084
|
enqueueWork(input: EnqueueWorkInput): WorkItemRecord {
|
|
3011
3085
|
if (!input.idempotencyKey.trim()) throw new Error('Work idempotency key is required');
|
|
3012
3086
|
if (!input.projectRoot.trim()) throw new Error('Work project root is required');
|
|
3013
|
-
const maxAttempts = input.maxAttempts
|
|
3087
|
+
const maxAttempts = input.maxAttempts
|
|
3088
|
+
?? (input.kind === 'knowledge.ingest' || input.kind === 'knowledge.index_refresh' ? 3 : 5);
|
|
3014
3089
|
if (!Number.isSafeInteger(maxAttempts) || maxAttempts <= 0) {
|
|
3015
3090
|
throw new Error('Work maxAttempts must be a positive safe integer');
|
|
3016
3091
|
}
|
|
@@ -3023,10 +3098,10 @@ export class DevFlowDatabase {
|
|
|
3023
3098
|
|
|
3024
3099
|
const row = this.db.prepare(`
|
|
3025
3100
|
INSERT INTO devflow_work_items (
|
|
3026
|
-
id, idempotency_key, kind, project_root, session_id, turn_id, payload,
|
|
3101
|
+
id, idempotency_key, kind, project_root, session_id, turn_id, source_hash, task_spec_hash, payload,
|
|
3027
3102
|
state, attempts, max_attempts, lease_owner, lease_expires_at,
|
|
3028
3103
|
next_attempt_at, error_category, error_message, created_at, updated_at, completed_at
|
|
3029
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', 0, ?, NULL, NULL, ?, NULL, NULL, ?, ?, NULL)
|
|
3104
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', 0, ?, NULL, NULL, ?, NULL, NULL, ?, ?, NULL)
|
|
3030
3105
|
ON CONFLICT(idempotency_key) DO UPDATE SET
|
|
3031
3106
|
state = CASE
|
|
3032
3107
|
WHEN devflow_work_items.state = 'dead_letter'
|
|
@@ -3067,6 +3142,8 @@ export class DevFlowDatabase {
|
|
|
3067
3142
|
input.projectRoot,
|
|
3068
3143
|
input.sessionId ?? null,
|
|
3069
3144
|
input.turnId ?? null,
|
|
3145
|
+
input.sourceHash ?? null,
|
|
3146
|
+
input.taskSpecHash ?? null,
|
|
3070
3147
|
JSON.stringify(input.payload ?? null),
|
|
3071
3148
|
maxAttempts,
|
|
3072
3149
|
nextAttemptAt,
|
|
@@ -3084,6 +3161,53 @@ export class DevFlowDatabase {
|
|
|
3084
3161
|
return row ? this.mapWorkItem(row) : null;
|
|
3085
3162
|
}
|
|
3086
3163
|
|
|
3164
|
+
getWorkById(id: string): WorkItemRecord | null {
|
|
3165
|
+
const row = this.db.prepare('SELECT * FROM devflow_work_items WHERE id = ?').get(id) as any;
|
|
3166
|
+
return row ? this.mapWorkItem(row) : null;
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3169
|
+
acquireProjectMutationLease(input: { projectRoot: string; mutationKind: string; owner: string; leaseMs: number; sourceHash?: string; now?: number }): boolean {
|
|
3170
|
+
const now = input.now ?? Date.now();
|
|
3171
|
+
return this.db.prepare(`
|
|
3172
|
+
INSERT INTO devflow_project_mutation_leases
|
|
3173
|
+
(project_root, mutation_kind, owner, lease_expires_at, source_hash, updated_at)
|
|
3174
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
3175
|
+
ON CONFLICT(project_root, mutation_kind) DO UPDATE SET
|
|
3176
|
+
owner = excluded.owner, lease_expires_at = excluded.lease_expires_at,
|
|
3177
|
+
source_hash = excluded.source_hash, updated_at = excluded.updated_at
|
|
3178
|
+
WHERE devflow_project_mutation_leases.lease_expires_at <= ? OR devflow_project_mutation_leases.owner = excluded.owner
|
|
3179
|
+
`).run(input.projectRoot, input.mutationKind, input.owner, now + input.leaseMs, input.sourceHash ?? null, now, now).changes > 0;
|
|
3180
|
+
}
|
|
3181
|
+
|
|
3182
|
+
releaseProjectMutationLease(projectRoot: string, mutationKind: string, owner: string): boolean {
|
|
3183
|
+
return this.db.prepare('DELETE FROM devflow_project_mutation_leases WHERE project_root = ? AND mutation_kind = ? AND owner = ?')
|
|
3184
|
+
.run(projectRoot, mutationKind, owner).changes > 0;
|
|
3185
|
+
}
|
|
3186
|
+
|
|
3187
|
+
cancelStaleWork(input: { id: string; owner: string; sourceHash?: string; taskSpecHash?: string; reason?: string; now?: number }): boolean {
|
|
3188
|
+
const current = this.getWorkById(input.id);
|
|
3189
|
+
const stale = current && current.state === 'leased' && current.leaseOwner === input.owner
|
|
3190
|
+
&& ((input.sourceHash !== undefined && current.sourceHash !== input.sourceHash)
|
|
3191
|
+
|| (input.taskSpecHash !== undefined && current.taskSpecHash !== input.taskSpecHash));
|
|
3192
|
+
if (!stale) return false;
|
|
3193
|
+
const now = input.now ?? Date.now();
|
|
3194
|
+
try {
|
|
3195
|
+
return this.db.prepare(`UPDATE devflow_work_items SET state = 'cancelled', lease_owner = NULL, lease_expires_at = NULL, cancelled_at = ?, cancellation_reason = ?, updated_at = ? WHERE id = ? AND state = 'leased' AND lease_owner = ?`)
|
|
3196
|
+
.run(now, input.reason ?? 'stale_source_or_task_spec', now, input.id, input.owner).changes === 1;
|
|
3197
|
+
} catch {
|
|
3198
|
+
return this.db.prepare(`UPDATE devflow_work_items SET state = 'completed', lease_owner = NULL, lease_expires_at = NULL, cancelled_at = ?, cancellation_reason = ?, updated_at = ? WHERE id = ? AND state = 'leased' AND lease_owner = ?`)
|
|
3199
|
+
.run(now, input.reason ?? 'stale_source_or_task_spec', now, input.id, input.owner).changes === 1;
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
|
|
3203
|
+
replayDeadLetterWork(id: string, input: { idempotencyKey: string; sourceHash?: string; taskSpecHash?: string; now?: number }): WorkItemRecord | null {
|
|
3204
|
+
const original = this.getWorkById(id);
|
|
3205
|
+
if (!original || original.state !== 'dead_letter') return null;
|
|
3206
|
+
const replay = this.enqueueWork({ idempotencyKey: input.idempotencyKey, kind: original.kind, projectRoot: original.projectRoot, sessionId: original.sessionId, turnId: original.turnId, payload: original.payload, sourceHash: input.sourceHash ?? original.sourceHash, taskSpecHash: input.taskSpecHash ?? original.taskSpecHash, maxAttempts: original.maxAttempts, nextAttemptAt: input.now ?? Date.now() });
|
|
3207
|
+
this.db.prepare('UPDATE devflow_work_items SET replay_of_id = ?, replay_count = replay_count + 1 WHERE id = ?').run(original.id, replay.id);
|
|
3208
|
+
return this.getWorkById(replay.id);
|
|
3209
|
+
}
|
|
3210
|
+
|
|
3087
3211
|
requestSessionClosure(input: RequestSessionClosureInput): SessionClosureRecord {
|
|
3088
3212
|
if (!input.sessionId.trim()) throw new Error('Session closure requires a session ID');
|
|
3089
3213
|
if (!input.projectRoot.trim()) throw new Error('Session closure requires a project root');
|
|
@@ -3387,8 +3511,10 @@ export class DevFlowDatabase {
|
|
|
3387
3511
|
projectRoot: row.project_root,
|
|
3388
3512
|
sessionId: row.session_id ?? undefined,
|
|
3389
3513
|
turnId: row.turn_id ?? undefined,
|
|
3514
|
+
sourceHash: row.source_hash ?? undefined,
|
|
3515
|
+
taskSpecHash: row.task_spec_hash ?? undefined,
|
|
3390
3516
|
payload: parseJson(row.payload),
|
|
3391
|
-
state: row.state,
|
|
3517
|
+
state: row.cancelled_at != null ? 'cancelled' : row.state,
|
|
3392
3518
|
attempts: Number(row.attempts),
|
|
3393
3519
|
maxAttempts: Number(row.max_attempts),
|
|
3394
3520
|
leaseOwner: row.lease_owner ?? undefined,
|
|
@@ -3399,6 +3525,10 @@ export class DevFlowDatabase {
|
|
|
3399
3525
|
createdAt: Number(row.created_at),
|
|
3400
3526
|
updatedAt: Number(row.updated_at),
|
|
3401
3527
|
completedAt: row.completed_at == null ? undefined : Number(row.completed_at),
|
|
3528
|
+
cancelledAt: row.cancelled_at == null ? undefined : Number(row.cancelled_at),
|
|
3529
|
+
cancellationReason: row.cancellation_reason ?? undefined,
|
|
3530
|
+
replayOfId: row.replay_of_id ?? undefined,
|
|
3531
|
+
replayCount: Number(row.replay_count ?? 0),
|
|
3402
3532
|
};
|
|
3403
3533
|
}
|
|
3404
3534
|
|
|
@@ -3810,6 +3940,38 @@ export class DevFlowDatabase {
|
|
|
3810
3940
|
} : null;
|
|
3811
3941
|
}
|
|
3812
3942
|
|
|
3943
|
+
getContextReceiptForRequest(
|
|
3944
|
+
projectRoot: string,
|
|
3945
|
+
sessionId: string,
|
|
3946
|
+
requestId: string,
|
|
3947
|
+
): ContextReceiptRecord | null {
|
|
3948
|
+
const row = this.db.prepare(`
|
|
3949
|
+
SELECT project_root, session_id, execution_id, context_hash, issued_at, expires_at,
|
|
3950
|
+
selected_files, memory_ids, canonical_next_action, canonical_action_json,
|
|
3951
|
+
action_attempts, action_satisfied_at, action_degradation_json, request_id
|
|
3952
|
+
FROM devflow_context_receipts
|
|
3953
|
+
WHERE project_root = ? AND session_id = ? AND request_id = ?
|
|
3954
|
+
ORDER BY issued_at DESC
|
|
3955
|
+
LIMIT 1
|
|
3956
|
+
`).get(projectRoot, sessionId, requestId) as any;
|
|
3957
|
+
return row ? {
|
|
3958
|
+
projectRoot: row.project_root,
|
|
3959
|
+
sessionId: row.session_id,
|
|
3960
|
+
executionId: row.execution_id,
|
|
3961
|
+
contextHash: row.context_hash,
|
|
3962
|
+
issuedAt: row.issued_at,
|
|
3963
|
+
expiresAt: row.expires_at,
|
|
3964
|
+
selectedFiles: parseJsonStringArray(row.selected_files),
|
|
3965
|
+
memoryIds: parseJsonStringArray(row.memory_ids),
|
|
3966
|
+
canonicalNextAction: row.canonical_next_action ?? undefined,
|
|
3967
|
+
canonicalAction: parseJsonObject(row.canonical_action_json),
|
|
3968
|
+
actionAttempts: parseJsonArray(row.action_attempts),
|
|
3969
|
+
actionSatisfiedAt: row.action_satisfied_at ?? undefined,
|
|
3970
|
+
actionDegradation: parseJsonObject(row.action_degradation_json),
|
|
3971
|
+
requestId: row.request_id ?? undefined,
|
|
3972
|
+
} : null;
|
|
3973
|
+
}
|
|
3974
|
+
|
|
3813
3975
|
recordContextSelectionEvent(event: ContextSelectionEventRecord): boolean {
|
|
3814
3976
|
return this.db.prepare(`
|
|
3815
3977
|
INSERT OR IGNORE INTO devflow_context_selection_events
|
|
@@ -3842,7 +4004,8 @@ export class DevFlowDatabase {
|
|
|
3842
4004
|
id, project_root, session_id, execution_id, turn_id, request_id, context_receipt,
|
|
3843
4005
|
source_type, source_id, stage, rank, raw_score, normalized_score, final_score,
|
|
3844
4006
|
applicability, reason, tool_evidence, verification_receipt, payload, created_at
|
|
3845
|
-
|
|
4007
|
+
, schema_version, task_spec_hash, actor, source_version, source_content_hash, evidence_ids, reason_code
|
|
4008
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3846
4009
|
`).run(
|
|
3847
4010
|
event.id, event.projectRoot, event.sessionId, event.executionId ?? null, event.turnId ?? null,
|
|
3848
4011
|
event.requestId, event.contextReceipt, event.sourceType, event.sourceId, event.stage,
|
|
@@ -3850,6 +4013,9 @@ export class DevFlowDatabase {
|
|
|
3850
4013
|
event.finalScore ?? null, JSON.stringify(event.applicability), event.reason ?? null,
|
|
3851
4014
|
JSON.stringify(event.toolEvidence), event.verificationReceipt ?? null,
|
|
3852
4015
|
JSON.stringify(event.payload), event.createdAt,
|
|
4016
|
+
event.schemaVersion ?? 'retrieval-ledger-event.v1', event.taskSpecHash ?? null,
|
|
4017
|
+
event.actor ?? null, event.sourceVersion ?? null, event.sourceContentHash ?? null,
|
|
4018
|
+
JSON.stringify(event.evidenceIds ?? []), event.reasonCode ?? null,
|
|
3853
4019
|
).changes > 0;
|
|
3854
4020
|
}
|
|
3855
4021
|
|
|
@@ -4085,6 +4251,123 @@ export class DevFlowDatabase {
|
|
|
4085
4251
|
return this.getTerminalTransition(record.receiptId)!;
|
|
4086
4252
|
}
|
|
4087
4253
|
|
|
4254
|
+
appendTaskRuntimeEvent(event: TaskRuntimeEventRecord): TaskRuntimeEventRecord {
|
|
4255
|
+
const existing = this.getTaskRuntimeEvent(event.eventId);
|
|
4256
|
+
if (existing) {
|
|
4257
|
+
if (stableTaskRuntimeJson(existing) !== stableTaskRuntimeJson(event)) {
|
|
4258
|
+
throw new Error(`TASK_RUNTIME_EVENT_CONFLICT:${event.eventId}`);
|
|
4259
|
+
}
|
|
4260
|
+
return existing;
|
|
4261
|
+
}
|
|
4262
|
+
const atSequence = this.db.prepare(`
|
|
4263
|
+
SELECT event_id FROM devflow_task_runtime_events
|
|
4264
|
+
WHERE project_root = ? AND session_id = ? AND turn_id = ? AND sequence = ?
|
|
4265
|
+
`).get(event.identity.projectRoot, event.identity.sessionId, event.identity.turnId, event.sequence) as
|
|
4266
|
+
{ event_id?: string } | undefined;
|
|
4267
|
+
if (atSequence) throw new Error(`TASK_RUNTIME_SEQUENCE_CONFLICT:${event.sequence}`);
|
|
4268
|
+
try {
|
|
4269
|
+
this.db.prepare(`
|
|
4270
|
+
INSERT INTO devflow_task_runtime_events (
|
|
4271
|
+
event_id, schema_version, producer, producer_version, project_root, project_id,
|
|
4272
|
+
host_id, session_id, turn_id, request_id, execution_id, task_spec_hash,
|
|
4273
|
+
sequence, kind, payload_json, created_at
|
|
4274
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
4275
|
+
`).run(
|
|
4276
|
+
event.eventId, event.schemaVersion, event.producer, event.producerVersion,
|
|
4277
|
+
event.identity.projectRoot, event.identity.projectId, event.identity.hostId,
|
|
4278
|
+
event.identity.sessionId, event.identity.turnId, event.identity.requestId,
|
|
4279
|
+
event.identity.executionId ?? null, event.taskSpecHash, event.sequence,
|
|
4280
|
+
event.kind, stableTaskRuntimeJson(event.payload), event.createdAt,
|
|
4281
|
+
);
|
|
4282
|
+
} catch (error) {
|
|
4283
|
+
const concurrentEvent = this.getTaskRuntimeEvent(event.eventId);
|
|
4284
|
+
if (concurrentEvent) {
|
|
4285
|
+
if (stableTaskRuntimeJson(concurrentEvent) === stableTaskRuntimeJson(event)) return concurrentEvent;
|
|
4286
|
+
throw new Error(`TASK_RUNTIME_EVENT_CONFLICT:${event.eventId}`);
|
|
4287
|
+
}
|
|
4288
|
+
const concurrentSequence = this.db.prepare(`
|
|
4289
|
+
SELECT event_id FROM devflow_task_runtime_events
|
|
4290
|
+
WHERE project_root = ? AND session_id = ? AND turn_id = ? AND sequence = ?
|
|
4291
|
+
`).get(event.identity.projectRoot, event.identity.sessionId, event.identity.turnId, event.sequence);
|
|
4292
|
+
if (concurrentSequence) throw new Error(`TASK_RUNTIME_SEQUENCE_CONFLICT:${event.sequence}`);
|
|
4293
|
+
throw error;
|
|
4294
|
+
}
|
|
4295
|
+
return this.getTaskRuntimeEvent(event.eventId)!;
|
|
4296
|
+
}
|
|
4297
|
+
|
|
4298
|
+
getTaskRuntimeEvent(eventId: string): TaskRuntimeEventRecord | null {
|
|
4299
|
+
const row = this.db.prepare('SELECT * FROM devflow_task_runtime_events WHERE event_id = ?')
|
|
4300
|
+
.get(eventId) as Record<string, unknown> | undefined;
|
|
4301
|
+
return row ? mapTaskRuntimeEventRow(row) : null;
|
|
4302
|
+
}
|
|
4303
|
+
|
|
4304
|
+
listTaskRuntimeEvents(projectRoot: string, sessionId: string, turnId: string): TaskRuntimeEventRecord[] {
|
|
4305
|
+
return (this.db.prepare(`
|
|
4306
|
+
SELECT * FROM devflow_task_runtime_events
|
|
4307
|
+
WHERE project_root = ? AND session_id = ? AND turn_id = ?
|
|
4308
|
+
ORDER BY sequence ASC
|
|
4309
|
+
`).all(projectRoot, sessionId, turnId) as Array<Record<string, unknown>>)
|
|
4310
|
+
.map(mapTaskRuntimeEventRow);
|
|
4311
|
+
}
|
|
4312
|
+
|
|
4313
|
+
putTaskRuntimeSnapshot(snapshot: TaskRuntimeSnapshotRecord): TaskRuntimeSnapshotRecord {
|
|
4314
|
+
const current = this.getTaskRuntimeSnapshot(
|
|
4315
|
+
snapshot.identity.projectRoot, snapshot.identity.sessionId, snapshot.identity.turnId,
|
|
4316
|
+
);
|
|
4317
|
+
if (current && current.lastEventSequence > snapshot.lastEventSequence) {
|
|
4318
|
+
throw new Error('TASK_RUNTIME_SNAPSHOT_REGRESSION');
|
|
4319
|
+
}
|
|
4320
|
+
this.db.prepare(`
|
|
4321
|
+
INSERT INTO devflow_task_runtime_snapshots (
|
|
4322
|
+
project_root, session_id, turn_id, last_event_sequence, schema_version,
|
|
4323
|
+
snapshot_json, snapshot_hash, updated_at
|
|
4324
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
4325
|
+
ON CONFLICT(project_root, session_id, turn_id) DO UPDATE SET
|
|
4326
|
+
last_event_sequence = excluded.last_event_sequence,
|
|
4327
|
+
schema_version = excluded.schema_version,
|
|
4328
|
+
snapshot_json = excluded.snapshot_json,
|
|
4329
|
+
snapshot_hash = excluded.snapshot_hash,
|
|
4330
|
+
updated_at = excluded.updated_at
|
|
4331
|
+
WHERE excluded.last_event_sequence >= devflow_task_runtime_snapshots.last_event_sequence
|
|
4332
|
+
`).run(
|
|
4333
|
+
snapshot.identity.projectRoot, snapshot.identity.sessionId, snapshot.identity.turnId,
|
|
4334
|
+
snapshot.lastEventSequence, snapshot.schemaVersion, stableTaskRuntimeJson(snapshot),
|
|
4335
|
+
snapshot.snapshotHash, snapshot.updatedAt,
|
|
4336
|
+
);
|
|
4337
|
+
return this.getTaskRuntimeSnapshot(
|
|
4338
|
+
snapshot.identity.projectRoot, snapshot.identity.sessionId, snapshot.identity.turnId,
|
|
4339
|
+
)!;
|
|
4340
|
+
}
|
|
4341
|
+
|
|
4342
|
+
appendTaskRuntimeEventAndSnapshot(
|
|
4343
|
+
event: TaskRuntimeEventRecord,
|
|
4344
|
+
snapshot: TaskRuntimeSnapshotRecord,
|
|
4345
|
+
): TaskRuntimeSnapshotRecord {
|
|
4346
|
+
return this.db.transaction(() => {
|
|
4347
|
+
this.appendTaskRuntimeEvent(event);
|
|
4348
|
+
return this.putTaskRuntimeSnapshot(snapshot);
|
|
4349
|
+
});
|
|
4350
|
+
}
|
|
4351
|
+
|
|
4352
|
+
getTaskRuntimeSnapshot(
|
|
4353
|
+
projectRoot: string,
|
|
4354
|
+
sessionId: string,
|
|
4355
|
+
turnId: string,
|
|
4356
|
+
): TaskRuntimeSnapshotRecord | null {
|
|
4357
|
+
const row = this.db.prepare(`
|
|
4358
|
+
SELECT * FROM devflow_task_runtime_snapshots
|
|
4359
|
+
WHERE project_root = ? AND session_id = ? AND turn_id = ?
|
|
4360
|
+
`).get(projectRoot, sessionId, turnId) as Record<string, unknown> | undefined;
|
|
4361
|
+
return row ? mapTaskRuntimeSnapshotRow(row) : null;
|
|
4362
|
+
}
|
|
4363
|
+
|
|
4364
|
+
deleteTaskRuntimeSnapshot(projectRoot: string, sessionId: string, turnId: string): boolean {
|
|
4365
|
+
return this.db.prepare(`
|
|
4366
|
+
DELETE FROM devflow_task_runtime_snapshots
|
|
4367
|
+
WHERE project_root = ? AND session_id = ? AND turn_id = ?
|
|
4368
|
+
`).run(projectRoot, sessionId, turnId).changes > 0;
|
|
4369
|
+
}
|
|
4370
|
+
|
|
4088
4371
|
getTerminalTransition(receiptId: string): TerminalTransitionRecord | null {
|
|
4089
4372
|
const row = this.db.prepare(`
|
|
4090
4373
|
SELECT * FROM devflow_terminal_transitions WHERE receipt_id = ?
|
|
@@ -4156,7 +4439,11 @@ export class DevFlowDatabase {
|
|
|
4156
4439
|
id: row.id, projectRoot: row.project_root, sessionId: row.session_id,
|
|
4157
4440
|
executionId: row.execution_id ?? undefined, turnId: row.turn_id ?? undefined,
|
|
4158
4441
|
requestId: row.request_id, contextReceipt: row.context_receipt,
|
|
4159
|
-
|
|
4442
|
+
schemaVersion: row.schema_version ?? 'retrieval-ledger-event.v1',
|
|
4443
|
+
sourceType: row.source_type, sourceId: row.source_id, stage: row.stage === 'adopted' ? 'consumed' : row.stage,
|
|
4444
|
+
taskSpecHash: row.task_spec_hash ?? undefined, actor: row.actor ?? undefined,
|
|
4445
|
+
sourceVersion: row.source_version ?? undefined, sourceContentHash: row.source_content_hash ?? undefined,
|
|
4446
|
+
evidenceIds: parseJsonStringArray(row.evidence_ids), reasonCode: row.reason_code ?? undefined,
|
|
4160
4447
|
rank: row.rank ?? undefined, rawScore: row.raw_score ?? undefined,
|
|
4161
4448
|
normalizedScore: row.normalized_score ?? undefined, finalScore: row.final_score ?? undefined,
|
|
4162
4449
|
applicability: parseJsonStringArray(row.applicability), reason: row.reason ?? undefined,
|
|
@@ -4501,16 +4788,18 @@ export class DevFlowDatabase {
|
|
|
4501
4788
|
turnId: string;
|
|
4502
4789
|
receiptId: string;
|
|
4503
4790
|
reason: string;
|
|
4791
|
+
source?: string;
|
|
4504
4792
|
decidedAt?: number;
|
|
4505
4793
|
}): MemoryTurnRecord {
|
|
4506
4794
|
const turnId = normalizeTurnId(input.turnId);
|
|
4507
4795
|
this.db.prepare(`
|
|
4508
4796
|
UPDATE devflow_memory_turns
|
|
4509
|
-
SET status = 'skipped', receipt_id = ?, memory_ids = '[]', source =
|
|
4797
|
+
SET status = 'skipped', receipt_id = ?, memory_ids = '[]', source = ?,
|
|
4510
4798
|
reason = ?, decided_at = ?
|
|
4511
4799
|
WHERE turn_id = ? AND status = 'pending'
|
|
4512
4800
|
`).run(
|
|
4513
4801
|
input.receiptId,
|
|
4802
|
+
input.source ?? 'host_skip',
|
|
4514
4803
|
input.reason,
|
|
4515
4804
|
input.decidedAt ?? Date.now(),
|
|
4516
4805
|
turnId,
|
package/src/index.ts
CHANGED
|
@@ -31,6 +31,7 @@ export type {
|
|
|
31
31
|
WorkKind,
|
|
32
32
|
WorkQueueHealth,
|
|
33
33
|
WorkState,
|
|
34
|
+
ProjectMutationLeaseRecord,
|
|
34
35
|
} from './work-queue';
|
|
35
36
|
export type {
|
|
36
37
|
RetrievalLedgerEventRecord,
|
|
@@ -75,6 +76,22 @@ export {
|
|
|
75
76
|
mapTranscriptCheckpointRow,
|
|
76
77
|
stableSemanticJson,
|
|
77
78
|
} from './task-semantic-control';
|
|
79
|
+
export {
|
|
80
|
+
mapTaskRuntimeEventRow,
|
|
81
|
+
mapTaskRuntimeSnapshotRow,
|
|
82
|
+
stableTaskRuntimeHash,
|
|
83
|
+
stableTaskRuntimeJson,
|
|
84
|
+
} from './task-runtime';
|
|
85
|
+
export type {
|
|
86
|
+
TaskRuntimeChannelRecord,
|
|
87
|
+
TaskRuntimeChannelRequirement,
|
|
88
|
+
TaskRuntimeChannelStatus,
|
|
89
|
+
TaskRuntimeEventRecord,
|
|
90
|
+
TaskRuntimeIdentityRecord,
|
|
91
|
+
TaskRuntimeObligationRecord,
|
|
92
|
+
TaskRuntimeObligationState,
|
|
93
|
+
TaskRuntimeSnapshotRecord,
|
|
94
|
+
} from './task-runtime';
|
|
78
95
|
export type {
|
|
79
96
|
ChannelQueryPlanRecord,
|
|
80
97
|
TaskIdentityRecord,
|
package/src/retrieval-ledger.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export type RetrievalLedgerStage =
|
|
2
|
-
| 'eligible' | 'candidate' | 'selected' | 'exposed' | 'adopted'
|
|
3
|
-
| 'positive_outcome' | 'negative_outcome' | 'unknown' | 'rejected';
|
|
2
|
+
| 'eligible' | 'candidate' | 'selected' | 'exposed' | 'adopted' | 'consumed' | 'verified'
|
|
3
|
+
| 'positive_outcome' | 'negative_outcome' | 'unknown' | 'unknown_outcome' | 'rejected';
|
|
4
4
|
|
|
5
5
|
export type RetrievalLedgerSourceType = 'code' | 'memory' | 'knowledge' | 'action';
|
|
6
6
|
|
|
7
7
|
export interface RetrievalLedgerEventRecord {
|
|
8
|
+
schemaVersion?: 'retrieval-ledger-event.v1';
|
|
8
9
|
id: string;
|
|
9
10
|
projectRoot: string;
|
|
10
11
|
sessionId: string;
|
|
@@ -14,6 +15,12 @@ export interface RetrievalLedgerEventRecord {
|
|
|
14
15
|
contextReceipt: string;
|
|
15
16
|
sourceType: RetrievalLedgerSourceType;
|
|
16
17
|
sourceId: string;
|
|
18
|
+
taskSpecHash?: string;
|
|
19
|
+
actor?: string;
|
|
20
|
+
sourceVersion?: string;
|
|
21
|
+
sourceContentHash?: string;
|
|
22
|
+
evidenceIds?: string[];
|
|
23
|
+
reasonCode?: string;
|
|
17
24
|
stage: RetrievalLedgerStage;
|
|
18
25
|
rank?: number;
|
|
19
26
|
rawScore?: number;
|