@devflow-tools/database 0.17.0 → 0.17.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/CHANGELOG.md CHANGED
@@ -3,6 +3,20 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [0.17.1](https://github.com/shilongfeicool/dev-flow/compare/v0.16.28...v0.17.1) (2026-07-31)
7
+
8
+
9
+ ### Features
10
+
11
+ * **context:** preserve immutable task intent ([249023d](https://github.com/shilongfeicool/dev-flow/commit/249023d091970bdb6a2744ac467cdf594c353d00))
12
+ * **control:** add task semantic identity contracts ([480d213](https://github.com/shilongfeicool/dev-flow/commit/480d2138e8408f25b888228d5e20050a938c50ec))
13
+ * **hooks:** close turns with canonical receipts ([69a577a](https://github.com/shilongfeicool/dev-flow/commit/69a577ada60abaf6c729f9722715bfe6f310bb2f))
14
+ * **retrieval:** enforce actions and track verified outcomes ([5e2756a](https://github.com/shilongfeicool/dev-flow/commit/5e2756ababaa246cd78ea7bc6877f59e5972e5db))
15
+
16
+
17
+
18
+
19
+
6
20
  # [0.17.0](https://github.com/shilongfeicool/dev-flow/compare/v0.16.28...v0.17.0) (2026-07-30)
7
21
 
8
22
 
@@ -5,6 +5,7 @@ import { type AppendRetrievalCycleInput, type CreateRetrievalSessionInput, type
5
5
  import { type AddLearningCandidateEvidenceInput, type LearningCandidateEvidenceRecord, type LearningCandidateRecord, type LearningCandidateState, type LearningCandidateVersionRecord, type TransitionLearningCandidateInput, type UpsertLearningCandidateInput } from './learning-candidates';
6
6
  import { type CreateWorkflowWorkerInput, type EnqueueWorkflowMergeInput, type TransitionWorkflowWorkerInput, type WorkflowMergeRecord, type WorkflowMergeState, type WorkflowWorkerEventRecord, type WorkflowWorkerRecord } from './workflow-workers';
7
7
  import type { RetrievalLedgerEventRecord } from './retrieval-ledger';
8
+ import { type ChannelQueryPlanRecord, type TaskIntentArtifactRecord, type TerminalTransitionRecord, type ToolNameResolutionRecord, type TranscriptCheckpointRecord } from './task-semantic-control';
8
9
  export interface BenchmarkReportRecord {
9
10
  runId: string;
10
11
  suiteId: string;
@@ -420,6 +421,44 @@ export declare class DevFlowDatabase {
420
421
  getActiveContextReceipt(projectRoot: string, sessionId: string, executionId?: string, now?: number): ContextReceiptRecord | null;
421
422
  recordContextSelectionEvent(event: ContextSelectionEventRecord): boolean;
422
423
  appendRetrievalLedgerEvent(event: RetrievalLedgerEventRecord): boolean;
424
+ appendTaskIntentArtifact(record: TaskIntentArtifactRecord): TaskIntentArtifactRecord;
425
+ getTaskIntentArtifact(artifactHash: string): TaskIntentArtifactRecord | null;
426
+ getLatestTaskIntentArtifact(input: {
427
+ projectRoot: string;
428
+ sessionId: string;
429
+ turnId?: string;
430
+ requestId?: string;
431
+ }): TaskIntentArtifactRecord | null;
432
+ getTaskIntentArtifactBySource(input: {
433
+ projectRoot: string;
434
+ sessionId: string;
435
+ sourceHash: string;
436
+ }): TaskIntentArtifactRecord | null;
437
+ listLatestTaskIntentArtifacts(projectRoot: string, sessionId: string): TaskIntentArtifactRecord[];
438
+ appendChannelQueryPlan(record: ChannelQueryPlanRecord): ChannelQueryPlanRecord;
439
+ getChannelQueryPlan(planHash: string): ChannelQueryPlanRecord | null;
440
+ getChannelQueryPlanForIntent(input: {
441
+ projectRoot: string;
442
+ sessionId: string;
443
+ turnId: string;
444
+ sourceIntentHash: string;
445
+ }): ChannelQueryPlanRecord | null;
446
+ appendToolNameResolution(record: ToolNameResolutionRecord): boolean;
447
+ listToolNameResolutions(input: {
448
+ projectRoot: string;
449
+ sessionId: string;
450
+ requestId?: string;
451
+ }): ToolNameResolutionRecord[];
452
+ appendTerminalTransition(record: TerminalTransitionRecord): TerminalTransitionRecord;
453
+ getTerminalTransition(receiptId: string): TerminalTransitionRecord | null;
454
+ getLatestTerminalTransition(projectRoot: string, sessionId: string, turnId: string): TerminalTransitionRecord | null;
455
+ appendTranscriptCheckpoint(record: TranscriptCheckpointRecord): boolean;
456
+ getLatestTranscriptCheckpoint(input: {
457
+ projectRoot: string;
458
+ hostId: string;
459
+ sessionId: string;
460
+ sourcePath: string;
461
+ }): TranscriptCheckpointRecord | null;
423
462
  listRetrievalLedgerEvents(options: {
424
463
  projectRoot: string;
425
464
  sessionId?: string;
package/dist/database.js CHANGED
@@ -13,6 +13,7 @@ const host_actions_1 = require("./host-actions");
13
13
  const retrieval_sessions_1 = require("./retrieval-sessions");
14
14
  const learning_candidates_1 = require("./learning-candidates");
15
15
  const workflow_workers_1 = require("./workflow-workers");
16
+ const task_semantic_control_1 = require("./task-semantic-control");
16
17
  const CONTEXT_REQUIRED_SKILLS = new Set([
17
18
  'react', 'vue', 'nest', 'nextjs', 'graphql', 'typescript',
18
19
  ]);
@@ -329,6 +330,119 @@ class DevFlowDatabase {
329
330
  project_root, session_id, execution_id, request_id, context_receipt, source_type, source_id, created_at
330
331
  );
331
332
 
333
+ CREATE TABLE IF NOT EXISTS devflow_task_intent_artifacts (
334
+ artifact_hash TEXT PRIMARY KEY,
335
+ project_root TEXT NOT NULL,
336
+ project_id TEXT NOT NULL,
337
+ host_id TEXT NOT NULL,
338
+ session_id TEXT NOT NULL,
339
+ turn_id TEXT NOT NULL,
340
+ request_id TEXT NOT NULL,
341
+ execution_id TEXT,
342
+ version INTEGER NOT NULL CHECK(version > 0),
343
+ supersedes_hash TEXT,
344
+ raw_prompt TEXT NOT NULL,
345
+ normalized_prompt TEXT NOT NULL,
346
+ command TEXT,
347
+ slash_args_json TEXT NOT NULL DEFAULT '[]',
348
+ active_skill TEXT,
349
+ intent TEXT NOT NULL,
350
+ action TEXT NOT NULL,
351
+ entities_json TEXT NOT NULL DEFAULT '[]',
352
+ target_anchors_json TEXT NOT NULL DEFAULT '[]',
353
+ policy_constraints_json TEXT NOT NULL DEFAULT '[]',
354
+ classification_evidence_json TEXT NOT NULL DEFAULT '[]',
355
+ source_event_ids_json TEXT NOT NULL DEFAULT '[]',
356
+ source_hash TEXT NOT NULL,
357
+ created_at INTEGER NOT NULL,
358
+ UNIQUE(project_root, session_id, turn_id, version),
359
+ UNIQUE(project_root, session_id, request_id, version)
360
+ );
361
+ CREATE INDEX IF NOT EXISTS idx_task_intent_active
362
+ ON devflow_task_intent_artifacts(project_root, session_id, turn_id, version DESC);
363
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_task_intent_source
364
+ ON devflow_task_intent_artifacts(project_root, session_id, source_hash, version);
365
+
366
+ CREATE TABLE IF NOT EXISTS devflow_channel_query_plans (
367
+ plan_hash TEXT PRIMARY KEY,
368
+ source_intent_hash TEXT NOT NULL,
369
+ project_root TEXT NOT NULL,
370
+ project_id TEXT NOT NULL,
371
+ host_id TEXT NOT NULL,
372
+ session_id TEXT NOT NULL,
373
+ turn_id TEXT NOT NULL,
374
+ request_id TEXT NOT NULL,
375
+ execution_id TEXT,
376
+ code_queries_json TEXT NOT NULL DEFAULT '[]',
377
+ memory_queries_json TEXT NOT NULL DEFAULT '[]',
378
+ knowledge_queries_json TEXT NOT NULL DEFAULT '[]',
379
+ generated_by TEXT NOT NULL
380
+ CHECK(generated_by IN ('deterministic', 'host_semantic', 'hybrid')),
381
+ degradation_json TEXT,
382
+ created_at INTEGER NOT NULL,
383
+ UNIQUE(project_root, session_id, turn_id, source_intent_hash)
384
+ );
385
+ CREATE INDEX IF NOT EXISTS idx_channel_query_plan_request
386
+ ON devflow_channel_query_plans(project_root, session_id, request_id, created_at DESC);
387
+
388
+ CREATE TABLE IF NOT EXISTS devflow_tool_name_resolutions (
389
+ id TEXT PRIMARY KEY,
390
+ source_hash TEXT NOT NULL UNIQUE,
391
+ project_root TEXT NOT NULL,
392
+ project_id TEXT NOT NULL,
393
+ host_id TEXT NOT NULL,
394
+ session_id TEXT NOT NULL,
395
+ turn_id TEXT NOT NULL,
396
+ request_id TEXT NOT NULL,
397
+ execution_id TEXT,
398
+ requested_name TEXT NOT NULL,
399
+ canonical_name TEXT,
400
+ projected_name TEXT,
401
+ status TEXT NOT NULL CHECK(status IN ('canonical', 'alias', 'unsupported')),
402
+ attempt INTEGER NOT NULL CHECK(attempt >= 0),
403
+ capability_mechanism TEXT,
404
+ reason TEXT,
405
+ created_at INTEGER NOT NULL
406
+ );
407
+ CREATE INDEX IF NOT EXISTS idx_tool_resolution_request
408
+ ON devflow_tool_name_resolutions(project_root, session_id, request_id, created_at);
409
+
410
+ CREATE TABLE IF NOT EXISTS devflow_terminal_transitions (
411
+ receipt_id TEXT PRIMARY KEY,
412
+ project_root TEXT NOT NULL,
413
+ project_id TEXT NOT NULL,
414
+ host_id TEXT NOT NULL,
415
+ session_id TEXT NOT NULL,
416
+ turn_id TEXT NOT NULL,
417
+ request_id TEXT NOT NULL,
418
+ execution_id TEXT,
419
+ sequence INTEGER NOT NULL CHECK(sequence > 0),
420
+ from_state TEXT,
421
+ to_state TEXT NOT NULL,
422
+ source_receipt_id TEXT,
423
+ reason TEXT NOT NULL,
424
+ payload_json TEXT NOT NULL DEFAULT '{}',
425
+ created_at INTEGER NOT NULL,
426
+ UNIQUE(project_root, session_id, turn_id, sequence)
427
+ );
428
+ CREATE INDEX IF NOT EXISTS idx_terminal_transition_turn
429
+ ON devflow_terminal_transitions(project_root, session_id, turn_id, sequence);
430
+
431
+ CREATE TABLE IF NOT EXISTS devflow_transcript_checkpoints (
432
+ id TEXT PRIMARY KEY,
433
+ project_root TEXT NOT NULL,
434
+ host_id TEXT NOT NULL,
435
+ session_id TEXT NOT NULL,
436
+ source_path TEXT NOT NULL,
437
+ source_hash TEXT NOT NULL,
438
+ byte_offset INTEGER NOT NULL CHECK(byte_offset >= 0),
439
+ event_count INTEGER NOT NULL CHECK(event_count >= 0),
440
+ created_at INTEGER NOT NULL,
441
+ UNIQUE(project_root, host_id, session_id, source_path, source_hash)
442
+ );
443
+ CREATE INDEX IF NOT EXISTS idx_transcript_checkpoint_latest
444
+ ON devflow_transcript_checkpoints(project_root, host_id, session_id, source_path, created_at DESC);
445
+
332
446
  CREATE TABLE IF NOT EXISTS devflow_policy_verification_baselines (
333
447
  project_root TEXT NOT NULL,
334
448
  kind TEXT NOT NULL,
@@ -2950,6 +3064,232 @@ class DevFlowDatabase {
2950
3064
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2951
3065
  `).run(event.id, event.projectRoot, event.sessionId, event.executionId ?? null, event.turnId ?? null, event.requestId, event.contextReceipt, event.sourceType, event.sourceId, event.stage, event.rank ?? null, event.rawScore ?? null, event.normalizedScore ?? null, event.finalScore ?? null, JSON.stringify(event.applicability), event.reason ?? null, JSON.stringify(event.toolEvidence), event.verificationReceipt ?? null, JSON.stringify(event.payload), event.createdAt).changes > 0;
2952
3066
  }
3067
+ appendTaskIntentArtifact(record) {
3068
+ (0, task_semantic_control_1.assertTaskIdentity)(record);
3069
+ if (!Number.isSafeInteger(record.version) || record.version <= 0) {
3070
+ throw new Error('TASK_INTENT_INVALID_VERSION');
3071
+ }
3072
+ const existingByHash = this.getTaskIntentArtifact(record.artifactHash);
3073
+ if (existingByHash) {
3074
+ if ((0, task_semantic_control_1.stableSemanticJson)(existingByHash) !== (0, task_semantic_control_1.stableSemanticJson)(record)) {
3075
+ throw new Error(`TASK_INTENT_HASH_CONFLICT:${record.artifactHash}`);
3076
+ }
3077
+ return existingByHash;
3078
+ }
3079
+ this.db.exec('BEGIN IMMEDIATE');
3080
+ try {
3081
+ const conflicting = this.db.prepare(`
3082
+ SELECT * FROM devflow_task_intent_artifacts
3083
+ WHERE project_root = ? AND session_id = ? AND turn_id = ? AND version = ?
3084
+ `).get(record.projectRoot, record.sessionId, record.turnId, record.version);
3085
+ if (conflicting)
3086
+ throw new Error(`TASK_INTENT_VERSION_CONFLICT:${record.turnId}:${record.version}`);
3087
+ if (record.supersedesHash && !this.getTaskIntentArtifact(record.supersedesHash)) {
3088
+ throw new Error(`TASK_INTENT_SUPERSEDED_NOT_FOUND:${record.supersedesHash}`);
3089
+ }
3090
+ this.db.prepare(`
3091
+ INSERT INTO devflow_task_intent_artifacts (
3092
+ artifact_hash, project_root, project_id, host_id, session_id, turn_id, request_id,
3093
+ execution_id, version, supersedes_hash, raw_prompt, normalized_prompt, command,
3094
+ slash_args_json, active_skill, intent, action, entities_json, target_anchors_json,
3095
+ policy_constraints_json, classification_evidence_json, source_event_ids_json,
3096
+ source_hash, created_at
3097
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
3098
+ `).run(record.artifactHash, record.projectRoot, record.projectId, record.hostId, record.sessionId, record.turnId, record.requestId, record.executionId ?? null, record.version, record.supersedesHash ?? null, record.rawPrompt, record.normalizedPrompt, record.command ?? null, (0, task_semantic_control_1.stableSemanticJson)(record.slashArgs), record.activeSkill ?? null, record.intent, record.action, (0, task_semantic_control_1.stableSemanticJson)(record.entities), (0, task_semantic_control_1.stableSemanticJson)(record.targetAnchors), (0, task_semantic_control_1.stableSemanticJson)(record.policyConstraints), (0, task_semantic_control_1.stableSemanticJson)(record.classificationEvidence), (0, task_semantic_control_1.stableSemanticJson)(record.sourceEventIds), record.sourceHash, record.createdAt);
3099
+ const created = this.getTaskIntentArtifact(record.artifactHash);
3100
+ this.db.exec('COMMIT');
3101
+ return created;
3102
+ }
3103
+ catch (error) {
3104
+ try {
3105
+ this.db.exec('ROLLBACK');
3106
+ }
3107
+ catch { }
3108
+ throw error;
3109
+ }
3110
+ }
3111
+ getTaskIntentArtifact(artifactHash) {
3112
+ const row = this.db.prepare(`
3113
+ SELECT * FROM devflow_task_intent_artifacts WHERE artifact_hash = ?
3114
+ `).get(artifactHash);
3115
+ return row ? (0, task_semantic_control_1.mapTaskIntentArtifactRow)(row) : null;
3116
+ }
3117
+ getLatestTaskIntentArtifact(input) {
3118
+ if (!input.turnId && !input.requestId)
3119
+ throw new Error('TASK_INTENT_LOOKUP_IDENTITY_REQUIRED');
3120
+ const predicates = ['project_root = ?', 'session_id = ?'];
3121
+ const params = [input.projectRoot, input.sessionId];
3122
+ if (input.turnId) {
3123
+ predicates.push('turn_id = ?');
3124
+ params.push(input.turnId);
3125
+ }
3126
+ if (input.requestId) {
3127
+ predicates.push('request_id = ?');
3128
+ params.push(input.requestId);
3129
+ }
3130
+ const row = this.db.prepare(`
3131
+ SELECT * FROM devflow_task_intent_artifacts
3132
+ WHERE ${predicates.join(' AND ')} ORDER BY version DESC, created_at DESC LIMIT 1
3133
+ `).get(...params);
3134
+ return row ? (0, task_semantic_control_1.mapTaskIntentArtifactRow)(row) : null;
3135
+ }
3136
+ getTaskIntentArtifactBySource(input) {
3137
+ const row = this.db.prepare(`
3138
+ SELECT * FROM devflow_task_intent_artifacts
3139
+ WHERE project_root = ? AND session_id = ? AND source_hash = ?
3140
+ ORDER BY version DESC, created_at DESC LIMIT 1
3141
+ `).get(input.projectRoot, input.sessionId, input.sourceHash);
3142
+ return row ? (0, task_semantic_control_1.mapTaskIntentArtifactRow)(row) : null;
3143
+ }
3144
+ listLatestTaskIntentArtifacts(projectRoot, sessionId) {
3145
+ return this.db.prepare(`
3146
+ SELECT artifact.* FROM devflow_task_intent_artifacts artifact
3147
+ JOIN (
3148
+ SELECT turn_id, MAX(version) AS version
3149
+ FROM devflow_task_intent_artifacts
3150
+ WHERE project_root = ? AND session_id = ? GROUP BY turn_id
3151
+ ) latest ON latest.turn_id = artifact.turn_id AND latest.version = artifact.version
3152
+ WHERE artifact.project_root = ? AND artifact.session_id = ?
3153
+ ORDER BY artifact.created_at ASC, artifact.turn_id ASC
3154
+ `).all(projectRoot, sessionId, projectRoot, sessionId)
3155
+ .map(task_semantic_control_1.mapTaskIntentArtifactRow);
3156
+ }
3157
+ appendChannelQueryPlan(record) {
3158
+ (0, task_semantic_control_1.assertTaskIdentity)(record);
3159
+ const source = this.getTaskIntentArtifact(record.sourceIntentHash);
3160
+ if (!source)
3161
+ throw new Error(`QUERY_PLAN_INTENT_NOT_FOUND:${record.sourceIntentHash}`);
3162
+ if (!sameTaskIdentity(source, record))
3163
+ throw new Error('QUERY_PLAN_IDENTITY_MISMATCH');
3164
+ const existing = this.getChannelQueryPlan(record.planHash);
3165
+ if (existing) {
3166
+ if ((0, task_semantic_control_1.stableSemanticJson)(existing) !== (0, task_semantic_control_1.stableSemanticJson)(record)) {
3167
+ throw new Error(`QUERY_PLAN_HASH_CONFLICT:${record.planHash}`);
3168
+ }
3169
+ return existing;
3170
+ }
3171
+ try {
3172
+ this.db.prepare(`
3173
+ INSERT INTO devflow_channel_query_plans (
3174
+ plan_hash, source_intent_hash, project_root, project_id, host_id, session_id,
3175
+ turn_id, request_id, execution_id, code_queries_json, memory_queries_json,
3176
+ knowledge_queries_json, generated_by, degradation_json, created_at
3177
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
3178
+ `).run(record.planHash, record.sourceIntentHash, record.projectRoot, record.projectId, record.hostId, record.sessionId, record.turnId, record.requestId, record.executionId ?? null, (0, task_semantic_control_1.stableSemanticJson)(record.code), (0, task_semantic_control_1.stableSemanticJson)(record.memory), (0, task_semantic_control_1.stableSemanticJson)(record.knowledge), record.generatedBy, record.degradation ? (0, task_semantic_control_1.stableSemanticJson)(record.degradation) : null, record.createdAt);
3179
+ }
3180
+ catch (error) {
3181
+ if (String(error).includes('UNIQUE constraint failed')) {
3182
+ throw new Error(`QUERY_PLAN_SOURCE_CONFLICT:${record.sourceIntentHash}`);
3183
+ }
3184
+ throw error;
3185
+ }
3186
+ return this.getChannelQueryPlan(record.planHash);
3187
+ }
3188
+ getChannelQueryPlan(planHash) {
3189
+ const row = this.db.prepare(`
3190
+ SELECT * FROM devflow_channel_query_plans WHERE plan_hash = ?
3191
+ `).get(planHash);
3192
+ return row ? (0, task_semantic_control_1.mapChannelQueryPlanRow)(row) : null;
3193
+ }
3194
+ getChannelQueryPlanForIntent(input) {
3195
+ const row = this.db.prepare(`
3196
+ SELECT * FROM devflow_channel_query_plans
3197
+ WHERE project_root = ? AND session_id = ? AND turn_id = ? AND source_intent_hash = ?
3198
+ ORDER BY created_at DESC LIMIT 1
3199
+ `).get(input.projectRoot, input.sessionId, input.turnId, input.sourceIntentHash);
3200
+ return row ? (0, task_semantic_control_1.mapChannelQueryPlanRow)(row) : null;
3201
+ }
3202
+ appendToolNameResolution(record) {
3203
+ (0, task_semantic_control_1.assertTaskIdentity)(record);
3204
+ if (!Number.isSafeInteger(record.attempt) || record.attempt < 0) {
3205
+ throw new Error('TOOL_RESOLUTION_INVALID_ATTEMPT');
3206
+ }
3207
+ return this.db.prepare(`
3208
+ INSERT OR IGNORE INTO devflow_tool_name_resolutions (
3209
+ id, source_hash, project_root, project_id, host_id, session_id, turn_id,
3210
+ request_id, execution_id, requested_name, canonical_name, projected_name,
3211
+ status, attempt, capability_mechanism, reason, created_at
3212
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
3213
+ `).run(record.id, record.sourceHash, record.projectRoot, record.projectId, record.hostId, record.sessionId, record.turnId, record.requestId, record.executionId ?? null, record.requestedName, record.canonicalName ?? null, record.projectedName ?? null, record.status, record.attempt, record.capabilityMechanism ?? null, record.reason ?? null, record.createdAt).changes > 0;
3214
+ }
3215
+ listToolNameResolutions(input) {
3216
+ const predicates = ['project_root = ?', 'session_id = ?'];
3217
+ const params = [input.projectRoot, input.sessionId];
3218
+ if (input.requestId) {
3219
+ predicates.push('request_id = ?');
3220
+ params.push(input.requestId);
3221
+ }
3222
+ return this.db.prepare(`
3223
+ SELECT * FROM devflow_tool_name_resolutions WHERE ${predicates.join(' AND ')}
3224
+ ORDER BY created_at ASC, attempt ASC, id ASC
3225
+ `).all(...params).map(task_semantic_control_1.mapToolNameResolutionRow);
3226
+ }
3227
+ appendTerminalTransition(record) {
3228
+ (0, task_semantic_control_1.assertTaskIdentity)(record);
3229
+ if (!Number.isSafeInteger(record.sequence) || record.sequence <= 0) {
3230
+ throw new Error('TERMINAL_TRANSITION_INVALID_SEQUENCE');
3231
+ }
3232
+ const existing = this.getTerminalTransition(record.receiptId);
3233
+ if (existing) {
3234
+ if ((0, task_semantic_control_1.stableSemanticJson)(existing) !== (0, task_semantic_control_1.stableSemanticJson)(record)) {
3235
+ throw new Error(`TERMINAL_RECEIPT_CONFLICT:${record.receiptId}`);
3236
+ }
3237
+ return existing;
3238
+ }
3239
+ const latest = this.getLatestTerminalTransition(record.projectRoot, record.sessionId, record.turnId);
3240
+ if (record.sequence !== (latest?.sequence ?? 0) + 1) {
3241
+ throw new Error(`TERMINAL_TRANSITION_SEQUENCE:${latest?.sequence ?? 0}->${record.sequence}`);
3242
+ }
3243
+ if ((latest?.toState ?? undefined) !== record.fromState) {
3244
+ throw new Error(`TERMINAL_TRANSITION_FROM_MISMATCH:${record.fromState ?? 'none'}`);
3245
+ }
3246
+ this.db.prepare(`
3247
+ INSERT INTO devflow_terminal_transitions (
3248
+ receipt_id, project_root, project_id, host_id, session_id, turn_id, request_id,
3249
+ execution_id, sequence, from_state, to_state, source_receipt_id, reason,
3250
+ payload_json, created_at
3251
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
3252
+ `).run(record.receiptId, record.projectRoot, record.projectId, record.hostId, record.sessionId, record.turnId, record.requestId, record.executionId ?? null, record.sequence, record.fromState ?? null, record.toState, record.sourceReceiptId ?? null, record.reason, (0, task_semantic_control_1.stableSemanticJson)(record.payload), record.createdAt);
3253
+ return this.getTerminalTransition(record.receiptId);
3254
+ }
3255
+ getTerminalTransition(receiptId) {
3256
+ const row = this.db.prepare(`
3257
+ SELECT * FROM devflow_terminal_transitions WHERE receipt_id = ?
3258
+ `).get(receiptId);
3259
+ return row ? (0, task_semantic_control_1.mapTerminalTransitionRow)(row) : null;
3260
+ }
3261
+ getLatestTerminalTransition(projectRoot, sessionId, turnId) {
3262
+ const row = this.db.prepare(`
3263
+ SELECT * FROM devflow_terminal_transitions
3264
+ WHERE project_root = ? AND session_id = ? AND turn_id = ?
3265
+ ORDER BY sequence DESC LIMIT 1
3266
+ `).get(projectRoot, sessionId, turnId);
3267
+ return row ? (0, task_semantic_control_1.mapTerminalTransitionRow)(row) : null;
3268
+ }
3269
+ appendTranscriptCheckpoint(record) {
3270
+ if (!record.id || !record.projectRoot || !record.hostId || !record.sessionId
3271
+ || !record.sourcePath || !record.sourceHash) {
3272
+ throw new Error('TRANSCRIPT_CHECKPOINT_INVALID_IDENTITY');
3273
+ }
3274
+ if (!Number.isSafeInteger(record.byteOffset) || record.byteOffset < 0
3275
+ || !Number.isSafeInteger(record.eventCount) || record.eventCount < 0) {
3276
+ throw new Error('TRANSCRIPT_CHECKPOINT_INVALID_POSITION');
3277
+ }
3278
+ return this.db.prepare(`
3279
+ INSERT OR IGNORE INTO devflow_transcript_checkpoints (
3280
+ id, project_root, host_id, session_id, source_path, source_hash,
3281
+ byte_offset, event_count, created_at
3282
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
3283
+ `).run(record.id, record.projectRoot, record.hostId, record.sessionId, record.sourcePath, record.sourceHash, record.byteOffset, record.eventCount, record.createdAt).changes > 0;
3284
+ }
3285
+ getLatestTranscriptCheckpoint(input) {
3286
+ const row = this.db.prepare(`
3287
+ SELECT * FROM devflow_transcript_checkpoints
3288
+ WHERE project_root = ? AND host_id = ? AND session_id = ? AND source_path = ?
3289
+ ORDER BY created_at DESC, byte_offset DESC LIMIT 1
3290
+ `).get(input.projectRoot, input.hostId, input.sessionId, input.sourcePath);
3291
+ return row ? (0, task_semantic_control_1.mapTranscriptCheckpointRow)(row) : null;
3292
+ }
2953
3293
  listRetrievalLedgerEvents(options) {
2954
3294
  const predicates = ['project_root = ?'];
2955
3295
  const params = [options.projectRoot];
@@ -3922,3 +4262,12 @@ function canonicalWorkflowProjectRoot(projectRoot) {
3922
4262
  return resolved;
3923
4263
  }
3924
4264
  }
4265
+ function sameTaskIdentity(left, right) {
4266
+ return left.projectRoot === right.projectRoot
4267
+ && left.projectId === right.projectId
4268
+ && left.hostId === right.hostId
4269
+ && left.sessionId === right.sessionId
4270
+ && left.turnId === right.turnId
4271
+ && left.requestId === right.requestId
4272
+ && left.executionId === right.executionId;
4273
+ }
package/dist/index.d.ts CHANGED
@@ -7,6 +7,8 @@ export { RETRIEVAL_MAX_CYCLES, isRetrievalSessionState } from './retrieval-sessi
7
7
  export type { AppendRetrievalCycleInput, CreateRetrievalSessionInput, RetrievalCycleRecord, RetrievalGapKind, RetrievalGapRecord, RetrievalSessionRecord, RetrievalSessionState, } from './retrieval-sessions';
8
8
  export type { FailHostActionInput, HostActionRecord, HostActionState, ReportHostActionInput, RequestHostActionInput, StartHostActionInput, VerifyHostActionInput, } from './host-actions';
9
9
  export { mapSessionObligationRow, normalizeTurnId, } from './obligation-ledger';
10
+ export { assertTaskIdentity, mapChannelQueryPlanRow, mapTaskIntentArtifactRow, mapTerminalTransitionRow, mapToolNameResolutionRow, mapTranscriptCheckpointRow, stableSemanticJson, } from './task-semantic-control';
11
+ export type { ChannelQueryPlanRecord, TaskIdentityRecord, TaskIntentArtifactRecord, TerminalTransitionRecord, ToolNameResolutionRecord, ToolNameResolutionStatus, TranscriptCheckpointRecord, } from './task-semantic-control';
10
12
  export { LEGAL_WORKFLOW_WORKER_TRANSITIONS, TERMINAL_WORKFLOW_WORKER_STATES, mapWorkflowMergeRow, mapWorkflowWorkerEventRow, mapWorkflowWorkerRow, } from './workflow-workers';
11
13
  export type { CreateWorkflowWorkerInput, EnqueueWorkflowMergeInput, TransitionWorkflowWorkerInput, WorkflowMergeRecord, WorkflowMergeState, WorkflowWorkerEventRecord, WorkflowWorkerRecord, WorkflowWorkerState, } from './workflow-workers';
12
14
  export { mapLearningCandidateRow, mapLearningEvidenceRow, mapLearningVersionRow, stableLearningJson, } from './learning-candidates';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.stableLearningJson = exports.mapLearningVersionRow = exports.mapLearningEvidenceRow = exports.mapLearningCandidateRow = exports.mapWorkflowWorkerRow = exports.mapWorkflowWorkerEventRow = exports.mapWorkflowMergeRow = exports.TERMINAL_WORKFLOW_WORKER_STATES = exports.LEGAL_WORKFLOW_WORKER_TRANSITIONS = exports.normalizeTurnId = exports.mapSessionObligationRow = exports.isRetrievalSessionState = exports.RETRIEVAL_MAX_CYCLES = exports.serializeHostActionReport = exports.mapHostActionRow = exports.isHostActionState = exports.hostActionReportsEqual = exports.openGlobalDevFlowDatabase = exports.getGlobalDevFlowDbPath = exports.DevFlowDatabase = void 0;
3
+ exports.stableLearningJson = exports.mapLearningVersionRow = exports.mapLearningEvidenceRow = exports.mapLearningCandidateRow = exports.mapWorkflowWorkerRow = exports.mapWorkflowWorkerEventRow = exports.mapWorkflowMergeRow = exports.TERMINAL_WORKFLOW_WORKER_STATES = exports.LEGAL_WORKFLOW_WORKER_TRANSITIONS = exports.stableSemanticJson = exports.mapTranscriptCheckpointRow = exports.mapToolNameResolutionRow = exports.mapTerminalTransitionRow = exports.mapTaskIntentArtifactRow = exports.mapChannelQueryPlanRow = exports.assertTaskIdentity = exports.normalizeTurnId = exports.mapSessionObligationRow = exports.isRetrievalSessionState = exports.RETRIEVAL_MAX_CYCLES = exports.serializeHostActionReport = exports.mapHostActionRow = exports.isHostActionState = exports.hostActionReportsEqual = exports.openGlobalDevFlowDatabase = exports.getGlobalDevFlowDbPath = exports.DevFlowDatabase = void 0;
4
4
  var database_1 = require("./database");
5
5
  Object.defineProperty(exports, "DevFlowDatabase", { enumerable: true, get: function () { return database_1.DevFlowDatabase; } });
6
6
  Object.defineProperty(exports, "getGlobalDevFlowDbPath", { enumerable: true, get: function () { return database_1.getGlobalDevFlowDbPath; } });
@@ -16,6 +16,14 @@ Object.defineProperty(exports, "isRetrievalSessionState", { enumerable: true, ge
16
16
  var obligation_ledger_1 = require("./obligation-ledger");
17
17
  Object.defineProperty(exports, "mapSessionObligationRow", { enumerable: true, get: function () { return obligation_ledger_1.mapSessionObligationRow; } });
18
18
  Object.defineProperty(exports, "normalizeTurnId", { enumerable: true, get: function () { return obligation_ledger_1.normalizeTurnId; } });
19
+ var task_semantic_control_1 = require("./task-semantic-control");
20
+ Object.defineProperty(exports, "assertTaskIdentity", { enumerable: true, get: function () { return task_semantic_control_1.assertTaskIdentity; } });
21
+ Object.defineProperty(exports, "mapChannelQueryPlanRow", { enumerable: true, get: function () { return task_semantic_control_1.mapChannelQueryPlanRow; } });
22
+ Object.defineProperty(exports, "mapTaskIntentArtifactRow", { enumerable: true, get: function () { return task_semantic_control_1.mapTaskIntentArtifactRow; } });
23
+ Object.defineProperty(exports, "mapTerminalTransitionRow", { enumerable: true, get: function () { return task_semantic_control_1.mapTerminalTransitionRow; } });
24
+ Object.defineProperty(exports, "mapToolNameResolutionRow", { enumerable: true, get: function () { return task_semantic_control_1.mapToolNameResolutionRow; } });
25
+ Object.defineProperty(exports, "mapTranscriptCheckpointRow", { enumerable: true, get: function () { return task_semantic_control_1.mapTranscriptCheckpointRow; } });
26
+ Object.defineProperty(exports, "stableSemanticJson", { enumerable: true, get: function () { return task_semantic_control_1.stableSemanticJson; } });
19
27
  var workflow_workers_1 = require("./workflow-workers");
20
28
  Object.defineProperty(exports, "LEGAL_WORKFLOW_WORKER_TRANSITIONS", { enumerable: true, get: function () { return workflow_workers_1.LEGAL_WORKFLOW_WORKER_TRANSITIONS; } });
21
29
  Object.defineProperty(exports, "TERMINAL_WORKFLOW_WORKER_STATES", { enumerable: true, get: function () { return workflow_workers_1.TERMINAL_WORKFLOW_WORKER_STATES; } });
@@ -0,0 +1,79 @@
1
+ export interface TaskIdentityRecord {
2
+ projectRoot: string;
3
+ projectId: string;
4
+ hostId: string;
5
+ sessionId: string;
6
+ turnId: string;
7
+ requestId: string;
8
+ executionId?: string;
9
+ }
10
+ export interface TaskIntentArtifactRecord extends TaskIdentityRecord {
11
+ version: number;
12
+ supersedesHash?: string;
13
+ rawPrompt: string;
14
+ normalizedPrompt: string;
15
+ command?: string;
16
+ slashArgs: string[];
17
+ activeSkill?: string;
18
+ intent: string;
19
+ action: string;
20
+ entities: string[];
21
+ targetAnchors: string[];
22
+ policyConstraints: string[];
23
+ classificationEvidence: Array<Record<string, unknown>>;
24
+ sourceEventIds: string[];
25
+ sourceHash: string;
26
+ artifactHash: string;
27
+ createdAt: number;
28
+ }
29
+ export interface ChannelQueryPlanRecord extends TaskIdentityRecord {
30
+ sourceIntentHash: string;
31
+ planHash: string;
32
+ code: Array<Record<string, unknown>>;
33
+ memory: Array<Record<string, unknown>>;
34
+ knowledge: Array<Record<string, unknown>>;
35
+ generatedBy: 'deterministic' | 'host_semantic' | 'hybrid';
36
+ degradation?: Record<string, unknown>;
37
+ createdAt: number;
38
+ }
39
+ export type ToolNameResolutionStatus = 'canonical' | 'alias' | 'unsupported';
40
+ export interface ToolNameResolutionRecord extends TaskIdentityRecord {
41
+ id: string;
42
+ sourceHash: string;
43
+ requestedName: string;
44
+ canonicalName?: string;
45
+ projectedName?: string;
46
+ status: ToolNameResolutionStatus;
47
+ attempt: number;
48
+ capabilityMechanism?: string;
49
+ reason?: string;
50
+ createdAt: number;
51
+ }
52
+ export interface TerminalTransitionRecord extends TaskIdentityRecord {
53
+ sequence: number;
54
+ fromState?: string;
55
+ toState: string;
56
+ receiptId: string;
57
+ sourceReceiptId?: string;
58
+ reason: string;
59
+ payload: Record<string, unknown>;
60
+ createdAt: number;
61
+ }
62
+ export interface TranscriptCheckpointRecord {
63
+ id: string;
64
+ projectRoot: string;
65
+ hostId: string;
66
+ sessionId: string;
67
+ sourcePath: string;
68
+ sourceHash: string;
69
+ byteOffset: number;
70
+ eventCount: number;
71
+ createdAt: number;
72
+ }
73
+ export declare function stableSemanticJson(value: unknown): string;
74
+ export declare function mapTaskIntentArtifactRow(row: Record<string, unknown>): TaskIntentArtifactRecord;
75
+ export declare function mapChannelQueryPlanRow(row: Record<string, unknown>): ChannelQueryPlanRecord;
76
+ export declare function mapToolNameResolutionRow(row: Record<string, unknown>): ToolNameResolutionRecord;
77
+ export declare function mapTerminalTransitionRow(row: Record<string, unknown>): TerminalTransitionRecord;
78
+ export declare function mapTranscriptCheckpointRow(row: Record<string, unknown>): TranscriptCheckpointRecord;
79
+ export declare function assertTaskIdentity(record: TaskIdentityRecord): void;