@jamesaphoenix/tx-core 0.4.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/dist/db.d.ts +42 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +46 -0
- package/dist/db.js.map +1 -0
- package/dist/errors.d.ts +231 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +139 -0
- package/dist/errors.js.map +1 -0
- package/dist/id.d.ts +6 -0
- package/dist/id.d.ts.map +1 -0
- package/dist/id.js +21 -0
- package/dist/id.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/layer.d.ts +50 -0
- package/dist/layer.d.ts.map +1 -0
- package/dist/layer.js +155 -0
- package/dist/layer.js.map +1 -0
- package/dist/mappers/anchor.d.ts +14 -0
- package/dist/mappers/anchor.d.ts.map +1 -0
- package/dist/mappers/anchor.js +38 -0
- package/dist/mappers/anchor.js.map +1 -0
- package/dist/mappers/attempt.d.ts +15 -0
- package/dist/mappers/attempt.d.ts.map +1 -0
- package/dist/mappers/attempt.js +23 -0
- package/dist/mappers/attempt.js.map +1 -0
- package/dist/mappers/candidate.d.ts +23 -0
- package/dist/mappers/candidate.d.ts.map +1 -0
- package/dist/mappers/candidate.js +53 -0
- package/dist/mappers/candidate.js.map +1 -0
- package/dist/mappers/claim.d.ts +30 -0
- package/dist/mappers/claim.d.ts.map +1 -0
- package/dist/mappers/claim.js +32 -0
- package/dist/mappers/claim.js.map +1 -0
- package/dist/mappers/deduplication.d.ts +39 -0
- package/dist/mappers/deduplication.d.ts.map +1 -0
- package/dist/mappers/deduplication.js +53 -0
- package/dist/mappers/deduplication.js.map +1 -0
- package/dist/mappers/edge.d.ts +10 -0
- package/dist/mappers/edge.d.ts.map +1 -0
- package/dist/mappers/edge.js +19 -0
- package/dist/mappers/edge.js.map +1 -0
- package/dist/mappers/file-learning.d.ts +14 -0
- package/dist/mappers/file-learning.d.ts.map +1 -0
- package/dist/mappers/file-learning.js +75 -0
- package/dist/mappers/file-learning.js.map +1 -0
- package/dist/mappers/index.d.ts +17 -0
- package/dist/mappers/index.d.ts.map +1 -0
- package/dist/mappers/index.js +30 -0
- package/dist/mappers/index.js.map +1 -0
- package/dist/mappers/learning.d.ts +19 -0
- package/dist/mappers/learning.d.ts.map +1 -0
- package/dist/mappers/learning.js +41 -0
- package/dist/mappers/learning.js.map +1 -0
- package/dist/mappers/orchestrator-state.d.ts +33 -0
- package/dist/mappers/orchestrator-state.d.ts.map +1 -0
- package/dist/mappers/orchestrator-state.js +34 -0
- package/dist/mappers/orchestrator-state.js.map +1 -0
- package/dist/mappers/run.d.ts +32 -0
- package/dist/mappers/run.d.ts.map +1 -0
- package/dist/mappers/run.js +64 -0
- package/dist/mappers/run.js.map +1 -0
- package/dist/mappers/task.d.ts +23 -0
- package/dist/mappers/task.d.ts.map +1 -0
- package/dist/mappers/task.js +54 -0
- package/dist/mappers/task.js.map +1 -0
- package/dist/mappers/tracked-project.d.ts +15 -0
- package/dist/mappers/tracked-project.d.ts.map +1 -0
- package/dist/mappers/tracked-project.js +23 -0
- package/dist/mappers/tracked-project.js.map +1 -0
- package/dist/mappers/worker.d.ts +33 -0
- package/dist/mappers/worker.d.ts.map +1 -0
- package/dist/mappers/worker.js +35 -0
- package/dist/mappers/worker.js.map +1 -0
- package/dist/repo/anchor-repo.d.ts +52 -0
- package/dist/repo/anchor-repo.d.ts.map +1 -0
- package/dist/repo/anchor-repo.js +204 -0
- package/dist/repo/anchor-repo.js.map +1 -0
- package/dist/repo/attempt-repo.d.ts +25 -0
- package/dist/repo/attempt-repo.d.ts.map +1 -0
- package/dist/repo/attempt-repo.js +78 -0
- package/dist/repo/attempt-repo.js.map +1 -0
- package/dist/repo/candidate-repo.d.ts +16 -0
- package/dist/repo/candidate-repo.d.ts.map +1 -0
- package/dist/repo/candidate-repo.js +143 -0
- package/dist/repo/candidate-repo.js.map +1 -0
- package/dist/repo/claim-repo.d.ts +17 -0
- package/dist/repo/claim-repo.d.ts.map +1 -0
- package/dist/repo/claim-repo.js +62 -0
- package/dist/repo/claim-repo.js.map +1 -0
- package/dist/repo/deduplication-repo.d.ts +37 -0
- package/dist/repo/deduplication-repo.d.ts.map +1 -0
- package/dist/repo/deduplication-repo.js +133 -0
- package/dist/repo/deduplication-repo.js.map +1 -0
- package/dist/repo/dep-repo.d.ts +19 -0
- package/dist/repo/dep-repo.d.ts.map +1 -0
- package/dist/repo/dep-repo.js +104 -0
- package/dist/repo/dep-repo.js.map +1 -0
- package/dist/repo/edge-repo.d.ts +26 -0
- package/dist/repo/edge-repo.d.ts.map +1 -0
- package/dist/repo/edge-repo.js +227 -0
- package/dist/repo/edge-repo.js.map +1 -0
- package/dist/repo/file-learning-repo.d.ts +17 -0
- package/dist/repo/file-learning-repo.d.ts.map +1 -0
- package/dist/repo/file-learning-repo.js +60 -0
- package/dist/repo/file-learning-repo.js.map +1 -0
- package/dist/repo/index.d.ts +18 -0
- package/dist/repo/index.d.ts.map +1 -0
- package/dist/repo/index.js +18 -0
- package/dist/repo/index.js.map +1 -0
- package/dist/repo/learning-repo.d.ts +31 -0
- package/dist/repo/learning-repo.d.ts.map +1 -0
- package/dist/repo/learning-repo.js +165 -0
- package/dist/repo/learning-repo.js.map +1 -0
- package/dist/repo/orchestrator-state-repo.d.ts +27 -0
- package/dist/repo/orchestrator-state-repo.d.ts.map +1 -0
- package/dist/repo/orchestrator-state-repo.js +96 -0
- package/dist/repo/orchestrator-state-repo.js.map +1 -0
- package/dist/repo/run-repo.d.ts +31 -0
- package/dist/repo/run-repo.d.ts.map +1 -0
- package/dist/repo/run-repo.js +132 -0
- package/dist/repo/run-repo.js.map +1 -0
- package/dist/repo/task-repo.d.ts +21 -0
- package/dist/repo/task-repo.d.ts.map +1 -0
- package/dist/repo/task-repo.js +169 -0
- package/dist/repo/task-repo.js.map +1 -0
- package/dist/repo/tracked-project-repo.d.ts +16 -0
- package/dist/repo/tracked-project-repo.d.ts.map +1 -0
- package/dist/repo/tracked-project-repo.js +54 -0
- package/dist/repo/tracked-project-repo.js.map +1 -0
- package/dist/repo/worker-repo.d.ts +19 -0
- package/dist/repo/worker-repo.d.ts.map +1 -0
- package/dist/repo/worker-repo.js +72 -0
- package/dist/repo/worker-repo.js.map +1 -0
- package/dist/schemas/index.d.ts +8 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +7 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/sync.d.ts +296 -0
- package/dist/schemas/sync.d.ts.map +1 -0
- package/dist/schemas/sync.js +146 -0
- package/dist/schemas/sync.js.map +1 -0
- package/dist/schemas/worker.d.ts +77 -0
- package/dist/schemas/worker.d.ts.map +1 -0
- package/dist/schemas/worker.js +80 -0
- package/dist/schemas/worker.js.map +1 -0
- package/dist/services/anchor-service.d.ts +147 -0
- package/dist/services/anchor-service.d.ts.map +1 -0
- package/dist/services/anchor-service.js +540 -0
- package/dist/services/anchor-service.js.map +1 -0
- package/dist/services/anchor-verification.d.ts +94 -0
- package/dist/services/anchor-verification.d.ts.map +1 -0
- package/dist/services/anchor-verification.js +617 -0
- package/dist/services/anchor-verification.js.map +1 -0
- package/dist/services/ast-grep-service.d.ts +58 -0
- package/dist/services/ast-grep-service.d.ts.map +1 -0
- package/dist/services/ast-grep-service.js +356 -0
- package/dist/services/ast-grep-service.js.map +1 -0
- package/dist/services/attempt-service.d.ts +24 -0
- package/dist/services/attempt-service.d.ts.map +1 -0
- package/dist/services/attempt-service.js +55 -0
- package/dist/services/attempt-service.js.map +1 -0
- package/dist/services/auto-sync-service.d.ts +56 -0
- package/dist/services/auto-sync-service.d.ts.map +1 -0
- package/dist/services/auto-sync-service.js +66 -0
- package/dist/services/auto-sync-service.js.map +1 -0
- package/dist/services/candidate-extractor-service.d.ts +56 -0
- package/dist/services/candidate-extractor-service.d.ts.map +1 -0
- package/dist/services/candidate-extractor-service.js +365 -0
- package/dist/services/candidate-extractor-service.js.map +1 -0
- package/dist/services/claim-service.d.ts +52 -0
- package/dist/services/claim-service.d.ts.map +1 -0
- package/dist/services/claim-service.js +134 -0
- package/dist/services/claim-service.js.map +1 -0
- package/dist/services/daemon-service.d.ts +214 -0
- package/dist/services/daemon-service.d.ts.map +1 -0
- package/dist/services/daemon-service.js +522 -0
- package/dist/services/daemon-service.js.map +1 -0
- package/dist/services/deduplication-service.d.ts +67 -0
- package/dist/services/deduplication-service.d.ts.map +1 -0
- package/dist/services/deduplication-service.js +145 -0
- package/dist/services/deduplication-service.js.map +1 -0
- package/dist/services/dep-service.d.ts +14 -0
- package/dist/services/dep-service.d.ts.map +1 -0
- package/dist/services/dep-service.js +34 -0
- package/dist/services/dep-service.js.map +1 -0
- package/dist/services/diversifier-service.d.ts +46 -0
- package/dist/services/diversifier-service.d.ts.map +1 -0
- package/dist/services/diversifier-service.js +197 -0
- package/dist/services/diversifier-service.js.map +1 -0
- package/dist/services/edge-service.d.ts +78 -0
- package/dist/services/edge-service.d.ts.map +1 -0
- package/dist/services/edge-service.js +158 -0
- package/dist/services/edge-service.js.map +1 -0
- package/dist/services/embedding-service.d.ts +138 -0
- package/dist/services/embedding-service.d.ts.map +1 -0
- package/dist/services/embedding-service.js +318 -0
- package/dist/services/embedding-service.js.map +1 -0
- package/dist/services/feedback-tracker.d.ts +64 -0
- package/dist/services/feedback-tracker.d.ts.map +1 -0
- package/dist/services/feedback-tracker.js +110 -0
- package/dist/services/feedback-tracker.js.map +1 -0
- package/dist/services/file-learning-service.d.ts +17 -0
- package/dist/services/file-learning-service.d.ts.map +1 -0
- package/dist/services/file-learning-service.js +41 -0
- package/dist/services/file-learning-service.js.map +1 -0
- package/dist/services/file-watcher-service.d.ts +141 -0
- package/dist/services/file-watcher-service.d.ts.map +1 -0
- package/dist/services/file-watcher-service.js +278 -0
- package/dist/services/file-watcher-service.js.map +1 -0
- package/dist/services/graph-expansion.d.ts +155 -0
- package/dist/services/graph-expansion.d.ts.map +1 -0
- package/dist/services/graph-expansion.js +466 -0
- package/dist/services/graph-expansion.js.map +1 -0
- package/dist/services/hierarchy-service.d.ts +16 -0
- package/dist/services/hierarchy-service.d.ts.map +1 -0
- package/dist/services/hierarchy-service.js +66 -0
- package/dist/services/hierarchy-service.js.map +1 -0
- package/dist/services/index.d.ts +36 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +36 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/learning-service.d.ts +39 -0
- package/dist/services/learning-service.d.ts.map +1 -0
- package/dist/services/learning-service.js +151 -0
- package/dist/services/learning-service.js.map +1 -0
- package/dist/services/migration-service.d.ts +67 -0
- package/dist/services/migration-service.d.ts.map +1 -0
- package/dist/services/migration-service.js +144 -0
- package/dist/services/migration-service.js.map +1 -0
- package/dist/services/orchestrator-service.d.ts +52 -0
- package/dist/services/orchestrator-service.d.ts.map +1 -0
- package/dist/services/orchestrator-service.js +203 -0
- package/dist/services/orchestrator-service.js.map +1 -0
- package/dist/services/promotion-service.d.ts +67 -0
- package/dist/services/promotion-service.d.ts.map +1 -0
- package/dist/services/promotion-service.js +151 -0
- package/dist/services/promotion-service.js.map +1 -0
- package/dist/services/query-expansion-service.d.ts +55 -0
- package/dist/services/query-expansion-service.d.ts.map +1 -0
- package/dist/services/query-expansion-service.js +174 -0
- package/dist/services/query-expansion-service.js.map +1 -0
- package/dist/services/ready-service.d.ts +16 -0
- package/dist/services/ready-service.d.ts.map +1 -0
- package/dist/services/ready-service.js +70 -0
- package/dist/services/ready-service.js.map +1 -0
- package/dist/services/reranker-service.d.ts +51 -0
- package/dist/services/reranker-service.d.ts.map +1 -0
- package/dist/services/reranker-service.js +128 -0
- package/dist/services/reranker-service.js.map +1 -0
- package/dist/services/retriever-service.d.ts +49 -0
- package/dist/services/retriever-service.d.ts.map +1 -0
- package/dist/services/retriever-service.js +419 -0
- package/dist/services/retriever-service.js.map +1 -0
- package/dist/services/score-service.d.ts +43 -0
- package/dist/services/score-service.d.ts.map +1 -0
- package/dist/services/score-service.js +82 -0
- package/dist/services/score-service.js.map +1 -0
- package/dist/services/swarm-verification.d.ts +104 -0
- package/dist/services/swarm-verification.d.ts.map +1 -0
- package/dist/services/swarm-verification.js +400 -0
- package/dist/services/swarm-verification.js.map +1 -0
- package/dist/services/sync-service.d.ts +115 -0
- package/dist/services/sync-service.d.ts.map +1 -0
- package/dist/services/sync-service.js +350 -0
- package/dist/services/sync-service.js.map +1 -0
- package/dist/services/task-service.d.ts +22 -0
- package/dist/services/task-service.d.ts.map +1 -0
- package/dist/services/task-service.js +221 -0
- package/dist/services/task-service.js.map +1 -0
- package/dist/services/worker-process.d.ts +41 -0
- package/dist/services/worker-process.d.ts.map +1 -0
- package/dist/services/worker-process.js +280 -0
- package/dist/services/worker-process.js.map +1 -0
- package/dist/services/worker-service.d.ts +74 -0
- package/dist/services/worker-service.d.ts.map +1 -0
- package/dist/services/worker-service.js +148 -0
- package/dist/services/worker-service.js.map +1 -0
- package/dist/utils/glob.d.ts +15 -0
- package/dist/utils/glob.d.ts.map +1 -0
- package/dist/utils/glob.js +27 -0
- package/dist/utils/glob.js.map +1 -0
- package/dist/utils/math.d.ts +6 -0
- package/dist/utils/math.d.ts.map +1 -0
- package/dist/utils/math.js +21 -0
- package/dist/utils/math.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AutoSyncService provides hooks for automatic JSONL export on data mutations.
|
|
3
|
+
*
|
|
4
|
+
* When auto-sync is enabled, any create/update/delete operation on tasks,
|
|
5
|
+
* learnings, file-learnings, attempts, or dependencies triggers an export
|
|
6
|
+
* to keep JSONL files in sync with SQLite.
|
|
7
|
+
*
|
|
8
|
+
* Design:
|
|
9
|
+
* - Non-blocking: Exports run in background fiber to avoid latency impact
|
|
10
|
+
* - Error-resilient: Export errors are logged but never propagate to caller
|
|
11
|
+
* - Configurable: Controlled via sync_config "auto_sync" setting
|
|
12
|
+
*/
|
|
13
|
+
import { Context, Effect, Layer } from "effect";
|
|
14
|
+
import { SqliteClient } from "../db.js";
|
|
15
|
+
import { SyncService } from "./sync-service.js";
|
|
16
|
+
import { DatabaseError } from "../errors.js";
|
|
17
|
+
export class AutoSyncService extends Context.Tag("AutoSyncService")() {
|
|
18
|
+
}
|
|
19
|
+
export const AutoSyncServiceLive = Layer.effect(AutoSyncService, Effect.gen(function* () {
|
|
20
|
+
const db = yield* SqliteClient;
|
|
21
|
+
const syncService = yield* SyncService;
|
|
22
|
+
// Check if auto-sync is enabled
|
|
23
|
+
const isEnabled = () => Effect.try({
|
|
24
|
+
try: () => {
|
|
25
|
+
const row = db.prepare("SELECT value FROM sync_config WHERE key = ?").get("auto_sync");
|
|
26
|
+
return row?.value === "true";
|
|
27
|
+
},
|
|
28
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
29
|
+
});
|
|
30
|
+
// Run export in background, catching and logging any errors
|
|
31
|
+
const runInBackground = (exportEffect) => Effect.gen(function* () {
|
|
32
|
+
const enabled = yield* isEnabled().pipe(Effect.catchAll(() => Effect.succeed(false)));
|
|
33
|
+
if (!enabled) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
// Fork the export and ignore the fiber - fire and forget
|
|
37
|
+
yield* Effect.fork(exportEffect.pipe(Effect.catchAll((error) => Effect.sync(() => {
|
|
38
|
+
// Log error but don't propagate
|
|
39
|
+
console.error("[auto-sync] Export failed:", error);
|
|
40
|
+
}))));
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
// Tasks use the standard export
|
|
44
|
+
afterTaskMutation: () => runInBackground(syncService.export()),
|
|
45
|
+
// TODO: Implement when learning sync is added to SyncService
|
|
46
|
+
afterLearningMutation: () => runInBackground(syncService.export()),
|
|
47
|
+
// TODO: Implement when file-learning sync is added to SyncService
|
|
48
|
+
afterFileLearningMutation: () => runInBackground(syncService.export()),
|
|
49
|
+
// TODO: Implement when attempt sync is added to SyncService
|
|
50
|
+
afterAttemptMutation: () => runInBackground(syncService.export()),
|
|
51
|
+
// For now, just exports tasks - extend when other entity types are supported
|
|
52
|
+
afterAnyMutation: () => runInBackground(syncService.export())
|
|
53
|
+
};
|
|
54
|
+
}));
|
|
55
|
+
/**
|
|
56
|
+
* Noop implementation for use when auto-sync is not needed.
|
|
57
|
+
* All hooks do nothing.
|
|
58
|
+
*/
|
|
59
|
+
export const AutoSyncServiceNoop = Layer.succeed(AutoSyncService, AutoSyncService.of({
|
|
60
|
+
afterTaskMutation: () => Effect.void,
|
|
61
|
+
afterLearningMutation: () => Effect.void,
|
|
62
|
+
afterFileLearningMutation: () => Effect.void,
|
|
63
|
+
afterAttemptMutation: () => Effect.void,
|
|
64
|
+
afterAnyMutation: () => Effect.void
|
|
65
|
+
}));
|
|
66
|
+
//# sourceMappingURL=auto-sync-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-sync-service.js","sourceRoot":"","sources":["../../src/services/auto-sync-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { ExtractionUnavailableError } from "../errors.js";
|
|
3
|
+
import type { TranscriptChunk, ExtractionResult } from "@jamesaphoenix/tx-types";
|
|
4
|
+
declare const CandidateExtractorService_base: Context.TagClass<CandidateExtractorService, "CandidateExtractorService", {
|
|
5
|
+
/**
|
|
6
|
+
* Extract learning candidates from a transcript chunk.
|
|
7
|
+
* Returns structured candidates with confidence and category.
|
|
8
|
+
*/
|
|
9
|
+
readonly extract: (chunk: TranscriptChunk) => Effect.Effect<ExtractionResult, ExtractionUnavailableError>;
|
|
10
|
+
/** Check if extraction functionality is available */
|
|
11
|
+
readonly isAvailable: () => Effect.Effect<boolean>;
|
|
12
|
+
}>;
|
|
13
|
+
/**
|
|
14
|
+
* CandidateExtractorService extracts learning candidates from Claude Code transcripts.
|
|
15
|
+
*
|
|
16
|
+
* This service uses LLM to analyze transcript content and identify:
|
|
17
|
+
* - Technical decisions with rationale
|
|
18
|
+
* - Gotchas and pitfalls to avoid
|
|
19
|
+
* - Patterns that worked well
|
|
20
|
+
* - Future improvements
|
|
21
|
+
*
|
|
22
|
+
* Design: Following DD-006 patterns for LLM integration with pluggable backends.
|
|
23
|
+
* Supports Anthropic, OpenAI, and noop backends with graceful degradation.
|
|
24
|
+
*
|
|
25
|
+
* @see PRD-015 for the JSONL daemon and knowledge promotion pipeline
|
|
26
|
+
*/
|
|
27
|
+
export declare class CandidateExtractorService extends CandidateExtractorService_base {
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Noop implementation - returns empty results without LLM processing.
|
|
31
|
+
* Used when no LLM API key is configured or for testing.
|
|
32
|
+
*/
|
|
33
|
+
export declare const CandidateExtractorServiceNoop: Layer.Layer<CandidateExtractorService, never, never>;
|
|
34
|
+
/**
|
|
35
|
+
* Anthropic (Claude) implementation.
|
|
36
|
+
* Uses Claude to extract learning candidates from transcripts.
|
|
37
|
+
*/
|
|
38
|
+
export declare const CandidateExtractorServiceAnthropic: Layer.Layer<CandidateExtractorService, ExtractionUnavailableError, never>;
|
|
39
|
+
/**
|
|
40
|
+
* OpenAI (GPT) implementation.
|
|
41
|
+
* Uses GPT to extract learning candidates from transcripts.
|
|
42
|
+
*/
|
|
43
|
+
export declare const CandidateExtractorServiceOpenAI: Layer.Layer<CandidateExtractorService, ExtractionUnavailableError, never>;
|
|
44
|
+
/**
|
|
45
|
+
* Auto-detecting layer that selects the appropriate backend based on environment.
|
|
46
|
+
*
|
|
47
|
+
* Priority:
|
|
48
|
+
* 1. ANTHROPIC_API_KEY set → Use Anthropic (Claude)
|
|
49
|
+
* 2. OPENAI_API_KEY set → Use OpenAI (GPT)
|
|
50
|
+
* 3. Neither set → Use Noop (graceful degradation)
|
|
51
|
+
*
|
|
52
|
+
* This allows graceful degradation when no LLM API keys are configured.
|
|
53
|
+
*/
|
|
54
|
+
export declare const CandidateExtractorServiceAuto: Layer.Layer<CandidateExtractorService, ExtractionUnavailableError, never>;
|
|
55
|
+
export {};
|
|
56
|
+
//# sourceMappingURL=candidate-extractor-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"candidate-extractor-service.d.ts","sourceRoot":"","sources":["../../src/services/candidate-extractor-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAkB,MAAM,QAAQ,CAAA;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EACV,eAAe,EAEf,gBAAgB,EAGjB,MAAM,yBAAyB,CAAA;;IAgL5B;;;OAGG;sBACe,CAAC,KAAK,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,0BAA0B,CAAC;IACzG,qDAAqD;0BAC/B,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;;AAvBtD;;;;;;;;;;;;;GAaG;AACH,qBAAa,yBAA0B,SAAQ,8BAW5C;CAAG;AAEN;;;GAGG;AACH,eAAO,MAAM,6BAA6B,sDAUzC,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,kCAAkC,2EA0G9C,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,+BAA+B,2EA4G3C,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,6BAA6B,2EAmBzC,CAAA"}
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Config, Option } from "effect";
|
|
2
|
+
import { ExtractionUnavailableError } from "../errors.js";
|
|
3
|
+
/**
|
|
4
|
+
* The prompt template for learning candidate extraction.
|
|
5
|
+
* Designed to extract actionable, generalizable learnings from Claude Code transcripts.
|
|
6
|
+
* @see PRD-015 for specification
|
|
7
|
+
*/
|
|
8
|
+
const EXTRACTION_PROMPT = `Analyze this Claude Code session transcript and extract actionable learnings.
|
|
9
|
+
|
|
10
|
+
<transcript>
|
|
11
|
+
{transcript_excerpt}
|
|
12
|
+
</transcript>
|
|
13
|
+
|
|
14
|
+
Extract learnings that meet these criteria:
|
|
15
|
+
1. **Technical decisions**: Describes a choice and its rationale
|
|
16
|
+
2. **Gotchas/pitfalls**: Something to avoid next time
|
|
17
|
+
3. **Patterns that worked**: Reusable approaches
|
|
18
|
+
4. **Future improvements**: Things to do differently
|
|
19
|
+
|
|
20
|
+
For each learning, provide:
|
|
21
|
+
- content: The learning text (1-3 sentences, actionable)
|
|
22
|
+
- confidence: "high" (certain, tested), "medium" (likely useful), "low" (speculative)
|
|
23
|
+
- category: One of [architecture, testing, performance, security, debugging, tooling, patterns, other]
|
|
24
|
+
|
|
25
|
+
Return JSON array:
|
|
26
|
+
[
|
|
27
|
+
{
|
|
28
|
+
"content": "Always wrap database operations in transactions to ensure atomicity",
|
|
29
|
+
"confidence": "high",
|
|
30
|
+
"category": "patterns"
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
Rules:
|
|
35
|
+
- Skip generic advice (things any developer knows)
|
|
36
|
+
- Skip context-specific details that won't generalize
|
|
37
|
+
- Prefer actionable "do X when Y" format
|
|
38
|
+
- Maximum 5 learnings per transcript
|
|
39
|
+
- If no meaningful learnings found, return empty array: []`;
|
|
40
|
+
/**
|
|
41
|
+
* Valid confidence levels for validation.
|
|
42
|
+
*/
|
|
43
|
+
const VALID_CONFIDENCES = ["high", "medium", "low"];
|
|
44
|
+
/**
|
|
45
|
+
* Valid categories for validation.
|
|
46
|
+
*/
|
|
47
|
+
const VALID_CATEGORIES = [
|
|
48
|
+
"architecture",
|
|
49
|
+
"testing",
|
|
50
|
+
"performance",
|
|
51
|
+
"security",
|
|
52
|
+
"debugging",
|
|
53
|
+
"tooling",
|
|
54
|
+
"patterns",
|
|
55
|
+
"other"
|
|
56
|
+
];
|
|
57
|
+
/**
|
|
58
|
+
* Parse LLM JSON response, handling common formatting issues.
|
|
59
|
+
* Robust parser following DD-006 patterns.
|
|
60
|
+
*/
|
|
61
|
+
const parseLlmJson = (raw) => {
|
|
62
|
+
// Step 1: Try direct parse
|
|
63
|
+
try {
|
|
64
|
+
return JSON.parse(raw);
|
|
65
|
+
}
|
|
66
|
+
catch { /* continue */ }
|
|
67
|
+
// Step 2: Strip markdown code fences
|
|
68
|
+
const fenceMatch = raw.match(/```(?:json)?\s*\n?([\s\S]*?)```/);
|
|
69
|
+
if (fenceMatch && fenceMatch[1]) {
|
|
70
|
+
try {
|
|
71
|
+
return JSON.parse(fenceMatch[1].trim());
|
|
72
|
+
}
|
|
73
|
+
catch { /* continue */ }
|
|
74
|
+
}
|
|
75
|
+
// Step 3: Find first [ or { and parse from there
|
|
76
|
+
const jsonStart = raw.search(/[[{]/);
|
|
77
|
+
if (jsonStart >= 0) {
|
|
78
|
+
const candidate = raw.slice(jsonStart);
|
|
79
|
+
try {
|
|
80
|
+
return JSON.parse(candidate);
|
|
81
|
+
}
|
|
82
|
+
catch { /* continue */ }
|
|
83
|
+
// Step 4: Find matching bracket and extract
|
|
84
|
+
const openChar = candidate[0];
|
|
85
|
+
const closeChar = openChar === "[" ? "]" : "}";
|
|
86
|
+
const lastClose = candidate.lastIndexOf(closeChar);
|
|
87
|
+
if (lastClose > 0) {
|
|
88
|
+
try {
|
|
89
|
+
return JSON.parse(candidate.slice(0, lastClose + 1));
|
|
90
|
+
}
|
|
91
|
+
catch { /* continue */ }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Validate and normalize an extracted candidate from LLM response.
|
|
98
|
+
* Returns null if the candidate is invalid.
|
|
99
|
+
*/
|
|
100
|
+
const validateCandidate = (raw) => {
|
|
101
|
+
if (!raw || typeof raw !== "object")
|
|
102
|
+
return null;
|
|
103
|
+
const obj = raw;
|
|
104
|
+
// Validate content
|
|
105
|
+
if (typeof obj.content !== "string" || obj.content.trim().length === 0)
|
|
106
|
+
return null;
|
|
107
|
+
const content = obj.content.trim();
|
|
108
|
+
// Skip if content is too short (likely not actionable)
|
|
109
|
+
if (content.length < 10)
|
|
110
|
+
return null;
|
|
111
|
+
// Validate confidence
|
|
112
|
+
const rawConfidence = String(obj.confidence || "").toLowerCase();
|
|
113
|
+
const confidence = VALID_CONFIDENCES.includes(rawConfidence)
|
|
114
|
+
? rawConfidence
|
|
115
|
+
: "medium"; // Default to medium if invalid
|
|
116
|
+
// Validate category
|
|
117
|
+
const rawCategory = String(obj.category || "").toLowerCase();
|
|
118
|
+
const category = VALID_CATEGORIES.includes(rawCategory)
|
|
119
|
+
? rawCategory
|
|
120
|
+
: "other"; // Default to other if invalid
|
|
121
|
+
return { content, confidence, category };
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* CandidateExtractorService extracts learning candidates from Claude Code transcripts.
|
|
125
|
+
*
|
|
126
|
+
* This service uses LLM to analyze transcript content and identify:
|
|
127
|
+
* - Technical decisions with rationale
|
|
128
|
+
* - Gotchas and pitfalls to avoid
|
|
129
|
+
* - Patterns that worked well
|
|
130
|
+
* - Future improvements
|
|
131
|
+
*
|
|
132
|
+
* Design: Following DD-006 patterns for LLM integration with pluggable backends.
|
|
133
|
+
* Supports Anthropic, OpenAI, and noop backends with graceful degradation.
|
|
134
|
+
*
|
|
135
|
+
* @see PRD-015 for the JSONL daemon and knowledge promotion pipeline
|
|
136
|
+
*/
|
|
137
|
+
export class CandidateExtractorService extends Context.Tag("CandidateExtractorService")() {
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Noop implementation - returns empty results without LLM processing.
|
|
141
|
+
* Used when no LLM API key is configured or for testing.
|
|
142
|
+
*/
|
|
143
|
+
export const CandidateExtractorServiceNoop = Layer.succeed(CandidateExtractorService, {
|
|
144
|
+
extract: (chunk) => Effect.succeed({
|
|
145
|
+
candidates: [],
|
|
146
|
+
sourceChunk: chunk,
|
|
147
|
+
wasExtracted: false
|
|
148
|
+
}),
|
|
149
|
+
isAvailable: () => Effect.succeed(false)
|
|
150
|
+
});
|
|
151
|
+
/**
|
|
152
|
+
* Anthropic (Claude) implementation.
|
|
153
|
+
* Uses Claude to extract learning candidates from transcripts.
|
|
154
|
+
*/
|
|
155
|
+
export const CandidateExtractorServiceAnthropic = Layer.effect(CandidateExtractorService, Effect.gen(function* () {
|
|
156
|
+
// Read API key from environment
|
|
157
|
+
const apiKey = yield* Config.string("ANTHROPIC_API_KEY").pipe(Effect.mapError(() => new ExtractionUnavailableError({
|
|
158
|
+
reason: "ANTHROPIC_API_KEY environment variable is not set"
|
|
159
|
+
})));
|
|
160
|
+
// Lazy-load client
|
|
161
|
+
let client = null;
|
|
162
|
+
const ensureClient = Effect.gen(function* () {
|
|
163
|
+
if (client)
|
|
164
|
+
return client;
|
|
165
|
+
// Dynamic import of Anthropic SDK (optional peer dependency)
|
|
166
|
+
const Anthropic = yield* Effect.tryPromise({
|
|
167
|
+
try: async () => {
|
|
168
|
+
// @ts-expect-error - @anthropic-ai/sdk is an optional peer dependency
|
|
169
|
+
const mod = await import("@anthropic-ai/sdk");
|
|
170
|
+
return mod.default;
|
|
171
|
+
},
|
|
172
|
+
catch: () => new ExtractionUnavailableError({
|
|
173
|
+
reason: "@anthropic-ai/sdk is not installed"
|
|
174
|
+
})
|
|
175
|
+
});
|
|
176
|
+
client = new Anthropic({ apiKey });
|
|
177
|
+
return client;
|
|
178
|
+
});
|
|
179
|
+
return {
|
|
180
|
+
extract: (chunk) => Effect.gen(function* () {
|
|
181
|
+
const startTime = Date.now();
|
|
182
|
+
const anthropic = yield* ensureClient;
|
|
183
|
+
// Build prompt with transcript content
|
|
184
|
+
const prompt = EXTRACTION_PROMPT.replace("{transcript_excerpt}", chunk.content);
|
|
185
|
+
const response = yield* Effect.tryPromise({
|
|
186
|
+
try: () => anthropic.messages.create({
|
|
187
|
+
model: "claude-haiku-4-20250514",
|
|
188
|
+
max_tokens: 1024,
|
|
189
|
+
messages: [{
|
|
190
|
+
role: "user",
|
|
191
|
+
content: prompt
|
|
192
|
+
}]
|
|
193
|
+
}),
|
|
194
|
+
catch: (e) => new ExtractionUnavailableError({
|
|
195
|
+
reason: `Anthropic API call failed: ${String(e)}`
|
|
196
|
+
})
|
|
197
|
+
});
|
|
198
|
+
// Extract text from response
|
|
199
|
+
const textContent = response.content.find(c => c.type === "text");
|
|
200
|
+
if (!textContent || !textContent.text) {
|
|
201
|
+
return {
|
|
202
|
+
candidates: [],
|
|
203
|
+
sourceChunk: chunk,
|
|
204
|
+
wasExtracted: true,
|
|
205
|
+
metadata: {
|
|
206
|
+
model: "claude-haiku-4-20250514",
|
|
207
|
+
tokensUsed: (response.usage?.input_tokens ?? 0) + (response.usage?.output_tokens ?? 0),
|
|
208
|
+
durationMs: Date.now() - startTime
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
// Parse the JSON array of candidates
|
|
213
|
+
const rawCandidates = parseLlmJson(textContent.text);
|
|
214
|
+
if (!rawCandidates || !Array.isArray(rawCandidates)) {
|
|
215
|
+
return {
|
|
216
|
+
candidates: [],
|
|
217
|
+
sourceChunk: chunk,
|
|
218
|
+
wasExtracted: true,
|
|
219
|
+
metadata: {
|
|
220
|
+
model: "claude-haiku-4-20250514",
|
|
221
|
+
tokensUsed: (response.usage?.input_tokens ?? 0) + (response.usage?.output_tokens ?? 0),
|
|
222
|
+
durationMs: Date.now() - startTime
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
// Validate and normalize each candidate
|
|
227
|
+
const candidates = rawCandidates
|
|
228
|
+
.map(validateCandidate)
|
|
229
|
+
.filter((c) => c !== null)
|
|
230
|
+
.slice(0, 5); // Max 5 candidates per chunk
|
|
231
|
+
return {
|
|
232
|
+
candidates,
|
|
233
|
+
sourceChunk: chunk,
|
|
234
|
+
wasExtracted: true,
|
|
235
|
+
metadata: {
|
|
236
|
+
model: "claude-haiku-4-20250514",
|
|
237
|
+
tokensUsed: (response.usage?.input_tokens ?? 0) + (response.usage?.output_tokens ?? 0),
|
|
238
|
+
durationMs: Date.now() - startTime
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}),
|
|
242
|
+
isAvailable: () => Effect.succeed(true)
|
|
243
|
+
};
|
|
244
|
+
}));
|
|
245
|
+
/**
|
|
246
|
+
* OpenAI (GPT) implementation.
|
|
247
|
+
* Uses GPT to extract learning candidates from transcripts.
|
|
248
|
+
*/
|
|
249
|
+
export const CandidateExtractorServiceOpenAI = Layer.effect(CandidateExtractorService, Effect.gen(function* () {
|
|
250
|
+
// Read API key from environment
|
|
251
|
+
const apiKey = yield* Config.string("OPENAI_API_KEY").pipe(Effect.mapError(() => new ExtractionUnavailableError({
|
|
252
|
+
reason: "OPENAI_API_KEY environment variable is not set"
|
|
253
|
+
})));
|
|
254
|
+
// Lazy-load client
|
|
255
|
+
let client = null;
|
|
256
|
+
const ensureClient = Effect.gen(function* () {
|
|
257
|
+
if (client)
|
|
258
|
+
return client;
|
|
259
|
+
// Dynamic import of OpenAI SDK (optional peer dependency)
|
|
260
|
+
const OpenAI = yield* Effect.tryPromise({
|
|
261
|
+
try: async () => {
|
|
262
|
+
// @ts-expect-error - openai is an optional peer dependency
|
|
263
|
+
const mod = await import("openai");
|
|
264
|
+
return mod.default;
|
|
265
|
+
},
|
|
266
|
+
catch: () => new ExtractionUnavailableError({
|
|
267
|
+
reason: "openai package is not installed"
|
|
268
|
+
})
|
|
269
|
+
});
|
|
270
|
+
client = new OpenAI({ apiKey });
|
|
271
|
+
return client;
|
|
272
|
+
});
|
|
273
|
+
return {
|
|
274
|
+
extract: (chunk) => Effect.gen(function* () {
|
|
275
|
+
const startTime = Date.now();
|
|
276
|
+
const openai = yield* ensureClient;
|
|
277
|
+
// Build prompt with transcript content
|
|
278
|
+
const prompt = EXTRACTION_PROMPT.replace("{transcript_excerpt}", chunk.content);
|
|
279
|
+
const response = yield* Effect.tryPromise({
|
|
280
|
+
try: () => openai.chat.completions.create({
|
|
281
|
+
model: "gpt-4o-mini",
|
|
282
|
+
max_tokens: 1024,
|
|
283
|
+
messages: [{
|
|
284
|
+
role: "user",
|
|
285
|
+
content: prompt
|
|
286
|
+
}],
|
|
287
|
+
response_format: { type: "json_object" }
|
|
288
|
+
}),
|
|
289
|
+
catch: (e) => new ExtractionUnavailableError({
|
|
290
|
+
reason: `OpenAI API call failed: ${String(e)}`
|
|
291
|
+
})
|
|
292
|
+
});
|
|
293
|
+
// Extract text from response
|
|
294
|
+
const textContent = response.choices[0]?.message?.content;
|
|
295
|
+
if (!textContent) {
|
|
296
|
+
return {
|
|
297
|
+
candidates: [],
|
|
298
|
+
sourceChunk: chunk,
|
|
299
|
+
wasExtracted: true,
|
|
300
|
+
metadata: {
|
|
301
|
+
model: "gpt-4o-mini",
|
|
302
|
+
tokensUsed: (response.usage?.prompt_tokens ?? 0) + (response.usage?.completion_tokens ?? 0),
|
|
303
|
+
durationMs: Date.now() - startTime
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
// Parse the JSON array of candidates
|
|
308
|
+
// OpenAI with json_object mode might wrap in an object
|
|
309
|
+
const parsed = parseLlmJson(textContent);
|
|
310
|
+
let rawCandidates = [];
|
|
311
|
+
if (Array.isArray(parsed)) {
|
|
312
|
+
rawCandidates = parsed;
|
|
313
|
+
}
|
|
314
|
+
else if (parsed && typeof parsed === "object") {
|
|
315
|
+
// Handle wrapped response like { candidates: [...] } or { learnings: [...] }
|
|
316
|
+
const obj = parsed;
|
|
317
|
+
const arrayField = Object.values(obj).find(Array.isArray);
|
|
318
|
+
if (arrayField) {
|
|
319
|
+
rawCandidates = arrayField;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
// Validate and normalize each candidate
|
|
323
|
+
const candidates = rawCandidates
|
|
324
|
+
.map(validateCandidate)
|
|
325
|
+
.filter((c) => c !== null)
|
|
326
|
+
.slice(0, 5); // Max 5 candidates per chunk
|
|
327
|
+
return {
|
|
328
|
+
candidates,
|
|
329
|
+
sourceChunk: chunk,
|
|
330
|
+
wasExtracted: true,
|
|
331
|
+
metadata: {
|
|
332
|
+
model: "gpt-4o-mini",
|
|
333
|
+
tokensUsed: (response.usage?.prompt_tokens ?? 0) + (response.usage?.completion_tokens ?? 0),
|
|
334
|
+
durationMs: Date.now() - startTime
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
}),
|
|
338
|
+
isAvailable: () => Effect.succeed(true)
|
|
339
|
+
};
|
|
340
|
+
}));
|
|
341
|
+
/**
|
|
342
|
+
* Auto-detecting layer that selects the appropriate backend based on environment.
|
|
343
|
+
*
|
|
344
|
+
* Priority:
|
|
345
|
+
* 1. ANTHROPIC_API_KEY set → Use Anthropic (Claude)
|
|
346
|
+
* 2. OPENAI_API_KEY set → Use OpenAI (GPT)
|
|
347
|
+
* 3. Neither set → Use Noop (graceful degradation)
|
|
348
|
+
*
|
|
349
|
+
* This allows graceful degradation when no LLM API keys are configured.
|
|
350
|
+
*/
|
|
351
|
+
export const CandidateExtractorServiceAuto = Layer.unwrapEffect(Effect.gen(function* () {
|
|
352
|
+
// Check for Anthropic API key first (preferred)
|
|
353
|
+
const anthropicKey = yield* Config.string("ANTHROPIC_API_KEY").pipe(Effect.option);
|
|
354
|
+
if (Option.isSome(anthropicKey) && anthropicKey.value.trim().length > 0) {
|
|
355
|
+
return CandidateExtractorServiceAnthropic;
|
|
356
|
+
}
|
|
357
|
+
// Fall back to OpenAI if available
|
|
358
|
+
const openaiKey = yield* Config.string("OPENAI_API_KEY").pipe(Effect.option);
|
|
359
|
+
if (Option.isSome(openaiKey) && openaiKey.value.trim().length > 0) {
|
|
360
|
+
return CandidateExtractorServiceOpenAI;
|
|
361
|
+
}
|
|
362
|
+
// No API keys configured - use noop
|
|
363
|
+
return CandidateExtractorServiceNoop;
|
|
364
|
+
}));
|
|
365
|
+
//# sourceMappingURL=candidate-extractor-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"candidate-extractor-service.js","sourceRoot":"","sources":["../../src/services/candidate-extractor-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClaimService - PRD-018
|
|
3
|
+
*
|
|
4
|
+
* Manages task claims with lease-based expiration.
|
|
5
|
+
* Uses Effect-TS patterns per DD-002.
|
|
6
|
+
*/
|
|
7
|
+
import { Context, Effect, Layer } from "effect";
|
|
8
|
+
import { ClaimRepository } from "../repo/claim-repo.js";
|
|
9
|
+
import { TaskRepository } from "../repo/task-repo.js";
|
|
10
|
+
import { OrchestratorStateRepository } from "../repo/orchestrator-state-repo.js";
|
|
11
|
+
import { AlreadyClaimedError, ClaimIdNotFoundError, ClaimNotFoundError, DatabaseError, LeaseExpiredError, MaxRenewalsExceededError, TaskNotFoundError } from "../errors.js";
|
|
12
|
+
import type { TaskClaim } from "../schemas/worker.js";
|
|
13
|
+
declare const ClaimService_base: Context.TagClass<ClaimService, "ClaimService", {
|
|
14
|
+
/**
|
|
15
|
+
* Claim a task for a worker with a lease.
|
|
16
|
+
* The lease duration is taken from orchestrator config or defaults to 30 minutes.
|
|
17
|
+
*/
|
|
18
|
+
readonly claim: (taskId: string, workerId: string, leaseDurationMinutes?: number) => Effect.Effect<TaskClaim, TaskNotFoundError | AlreadyClaimedError | DatabaseError>;
|
|
19
|
+
/**
|
|
20
|
+
* Release a claim on a task.
|
|
21
|
+
* Only the worker who holds the claim can release it.
|
|
22
|
+
*/
|
|
23
|
+
readonly release: (taskId: string, workerId: string) => Effect.Effect<void, ClaimNotFoundError | DatabaseError>;
|
|
24
|
+
/**
|
|
25
|
+
* Renew the lease on an existing claim.
|
|
26
|
+
* Fails if the claim is expired or max renewals exceeded.
|
|
27
|
+
*/
|
|
28
|
+
readonly renew: (taskId: string, workerId: string) => Effect.Effect<TaskClaim, ClaimNotFoundError | LeaseExpiredError | MaxRenewalsExceededError | DatabaseError>;
|
|
29
|
+
/**
|
|
30
|
+
* Get all claims that have expired but are still marked as active.
|
|
31
|
+
*/
|
|
32
|
+
readonly getExpired: () => Effect.Effect<readonly TaskClaim[], DatabaseError>;
|
|
33
|
+
/**
|
|
34
|
+
* Mark a claim as expired.
|
|
35
|
+
* Used during reconciliation to handle stale claims.
|
|
36
|
+
*/
|
|
37
|
+
readonly expire: (claimId: number) => Effect.Effect<void, ClaimIdNotFoundError | DatabaseError>;
|
|
38
|
+
/**
|
|
39
|
+
* Release all active claims held by a worker.
|
|
40
|
+
* Used during worker deregistration.
|
|
41
|
+
*/
|
|
42
|
+
readonly releaseByWorker: (workerId: string) => Effect.Effect<number, DatabaseError>;
|
|
43
|
+
/**
|
|
44
|
+
* Get the active claim for a task, if any.
|
|
45
|
+
*/
|
|
46
|
+
readonly getActiveClaim: (taskId: string) => Effect.Effect<TaskClaim | null, DatabaseError>;
|
|
47
|
+
}>;
|
|
48
|
+
export declare class ClaimService extends ClaimService_base {
|
|
49
|
+
}
|
|
50
|
+
export declare const ClaimServiceLive: Layer.Layer<ClaimService, never, TaskRepository | ClaimRepository | OrchestratorStateRepository>;
|
|
51
|
+
export {};
|
|
52
|
+
//# sourceMappingURL=claim-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-service.d.ts","sourceRoot":"","sources":["../../src/services/claim-service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAA;AAChF,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,iBAAiB,EACjB,wBAAwB,EACxB,iBAAiB,EAClB,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;;IAWjD;;;OAGG;oBACa,CACd,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,oBAAoB,CAAC,EAAE,MAAM,KAC1B,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,iBAAiB,GAAG,mBAAmB,GAAG,aAAa,CAAC;IAEtF;;;OAGG;sBACe,CAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,GAAG,aAAa,CAAC;IAE5D;;;OAGG;oBACa,CACd,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,kBAAkB,GAAG,iBAAiB,GAAG,wBAAwB,GAAG,aAAa,CAAC;IAEhH;;OAEG;yBACkB,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,SAAS,EAAE,EAAE,aAAa,CAAC;IAE7E;;;OAGG;qBACc,CACf,OAAO,EAAE,MAAM,KACZ,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,oBAAoB,GAAG,aAAa,CAAC;IAE9D;;;OAGG;8BACuB,CACxB,QAAQ,EAAE,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC;IAEzC;;OAEG;6BACsB,CACvB,MAAM,EAAE,MAAM,KACX,MAAM,CAAC,MAAM,CAAC,SAAS,GAAG,IAAI,EAAE,aAAa,CAAC;;AAzDvD,qBAAa,YAAa,SAAQ,iBA2D/B;CAAG;AAEN,eAAO,MAAM,gBAAgB,kGA6J5B,CAAA"}
|