@lota-sdk/core 0.4.8 → 0.4.10
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/package.json +11 -12
- package/src/ai/embedding-cache.ts +96 -22
- package/src/ai-gateway/ai-gateway.ts +766 -223
- package/src/config/agent-defaults.ts +189 -75
- package/src/config/agent-types.ts +54 -4
- package/src/config/background-processing.ts +1 -1
- package/src/config/constants.ts +8 -2
- package/src/config/index.ts +0 -1
- package/src/config/logger.ts +299 -19
- package/src/config/thread-defaults.ts +40 -20
- package/src/create-runtime.ts +200 -449
- package/src/db/base.service.ts +52 -28
- package/src/db/cursor-pagination.ts +71 -30
- package/src/db/memory-query-builder.ts +2 -1
- package/src/db/memory-store.helpers.ts +4 -7
- package/src/db/memory-store.ts +868 -601
- package/src/db/memory.ts +396 -280
- package/src/db/record-id.ts +32 -10
- package/src/db/schema-fingerprint.ts +30 -12
- package/src/db/service-normalization.ts +288 -0
- package/src/db/service.ts +912 -779
- package/src/db/startup.ts +153 -68
- package/src/db/transaction-conflict.ts +15 -0
- package/src/effect/awaitable-effect.ts +96 -0
- package/src/effect/errors.ts +121 -0
- package/src/effect/helpers.ts +123 -0
- package/src/effect/index.ts +24 -0
- package/src/effect/layers.ts +238 -0
- package/src/effect/runtime-ref.ts +25 -0
- package/src/effect/runtime.ts +46 -0
- package/src/effect/services.ts +61 -0
- package/src/effect/zod.ts +43 -0
- package/src/embeddings/provider.ts +128 -83
- package/src/index.ts +48 -1
- package/src/openrouter/direct-provider.ts +11 -35
- package/src/queues/autonomous-job.queue.ts +117 -73
- package/src/queues/context-compaction.queue.ts +50 -17
- package/src/queues/delayed-node-promotion.queue.ts +46 -17
- package/src/queues/document-processor.queue.ts +52 -77
- package/src/queues/memory-consolidation.queue.ts +47 -32
- package/src/queues/organization-learning.queue.ts +26 -4
- package/src/queues/plan-agent-heartbeat.queue.ts +71 -24
- package/src/queues/plan-scheduler.queue.ts +97 -33
- package/src/queues/post-chat-memory.queue.ts +56 -26
- package/src/queues/queue-factory.ts +227 -59
- package/src/queues/standalone-worker.ts +39 -0
- package/src/queues/title-generation.queue.ts +45 -11
- package/src/redis/connection.ts +182 -113
- package/src/redis/index.ts +6 -8
- package/src/redis/org-memory-lock.ts +60 -27
- package/src/redis/redis-lease-lock.ts +200 -121
- package/src/redis/runtime-connection.ts +20 -0
- package/src/redis/stream-context.ts +92 -46
- package/src/runtime/agent-identity-overrides.ts +2 -2
- package/src/runtime/agent-runtime-policy.ts +5 -2
- package/src/runtime/agent-stream-helpers.ts +24 -9
- package/src/runtime/chat-run-orchestration.ts +102 -19
- package/src/runtime/chat-run-registry.ts +36 -2
- package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
- package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +161 -94
- package/src/runtime/domain-layer.ts +192 -0
- package/src/runtime/execution-plan-visibility.ts +2 -2
- package/src/runtime/execution-plan.ts +42 -15
- package/src/runtime/graph-designer.ts +16 -4
- package/src/runtime/helper-model.ts +139 -48
- package/src/runtime/index.ts +7 -8
- package/src/runtime/indexed-repositories-policy.ts +3 -3
- package/src/runtime/{memory-block.ts → memory/memory-block.ts} +50 -36
- package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
- package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +54 -67
- package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
- package/src/runtime/memory/memory-scope.ts +53 -0
- package/src/runtime/plugin-resolution.ts +124 -25
- package/src/runtime/plugin-types.ts +9 -1
- package/src/runtime/post-turn-side-effects.ts +177 -130
- package/src/runtime/retrieval-adapters.ts +40 -6
- package/src/runtime/runtime-accessors.ts +92 -0
- package/src/runtime/runtime-config.ts +150 -61
- package/src/runtime/runtime-extensions.ts +23 -25
- package/src/runtime/runtime-lifecycle.ts +124 -0
- package/src/runtime/runtime-services.ts +386 -0
- package/src/runtime/runtime-token.ts +47 -0
- package/src/runtime/social-chat/social-chat-agent-runner.ts +159 -0
- package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +51 -20
- package/src/runtime/social-chat/social-chat.ts +630 -0
- package/src/runtime/specialist-runner.ts +36 -10
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +433 -0
- package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
- package/src/runtime/thread-chat-helpers.ts +2 -2
- package/src/runtime/thread-plan-turn.ts +2 -1
- package/src/runtime/thread-turn-context.ts +183 -111
- package/src/runtime/turn-lifecycle.ts +93 -27
- package/src/services/agent-activity.service.ts +287 -203
- package/src/services/agent-executor.service.ts +253 -149
- package/src/services/artifact.service.ts +231 -149
- package/src/services/attachment.service.ts +171 -115
- package/src/services/autonomous-job.service.ts +890 -491
- package/src/services/background-work.service.ts +54 -0
- package/src/services/chat-run-registry.service.ts +13 -1
- package/src/services/context-compaction.service.ts +136 -86
- package/src/services/document-chunk.service.ts +151 -88
- package/src/services/execution-plan/execution-plan-approval.ts +26 -0
- package/src/services/execution-plan/execution-plan-context.ts +29 -0
- package/src/services/execution-plan/execution-plan-graph.ts +278 -0
- package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
- package/src/services/execution-plan/execution-plan-spec.ts +75 -0
- package/src/services/execution-plan/execution-plan.service.ts +1041 -0
- package/src/services/feedback-loop.service.ts +132 -76
- package/src/services/global-orchestrator.service.ts +101 -168
- package/src/services/graph-full-routing.ts +193 -0
- package/src/services/index.ts +19 -21
- package/src/services/institutional-memory.service.ts +213 -125
- package/src/services/learned-skill.service.ts +368 -260
- package/src/services/memory/memory-conversation.ts +95 -0
- package/src/services/memory/memory-errors.ts +27 -0
- package/src/services/memory/memory-org-memory.ts +50 -0
- package/src/services/memory/memory-preseeded.ts +86 -0
- package/src/services/memory/memory-rerank.ts +297 -0
- package/src/services/{memory-utils.ts → memory/memory-utils.ts} +6 -5
- package/src/services/memory/memory.service.ts +674 -0
- package/src/services/memory/rerank.service.ts +201 -0
- package/src/services/monitoring-window.service.ts +92 -70
- package/src/services/mutating-approval.service.ts +62 -53
- package/src/services/node-workspace.service.ts +141 -98
- package/src/services/notification.service.ts +29 -16
- package/src/services/organization-member.service.ts +120 -66
- package/src/services/organization.service.ts +153 -77
- package/src/services/ownership-dispatcher.service.ts +456 -263
- package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
- package/src/services/plan/plan-agent-query.service.ts +322 -0
- package/src/services/{plan-approval.service.ts → plan/plan-approval.service.ts} +45 -22
- package/src/services/plan/plan-artifact.service.ts +60 -0
- package/src/services/plan/plan-builder.service.ts +76 -0
- package/src/services/plan/plan-checkpoint.service.ts +103 -0
- package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
- package/src/services/plan/plan-completion-side-effects.ts +169 -0
- package/src/services/plan/plan-coordination.service.ts +181 -0
- package/src/services/plan/plan-cycle.service.ts +405 -0
- package/src/services/plan/plan-deadline.service.ts +533 -0
- package/src/services/plan/plan-event-delivery.service.ts +266 -0
- package/src/services/plan/plan-executor-context.ts +35 -0
- package/src/services/plan/plan-executor-graph.ts +522 -0
- package/src/services/plan/plan-executor-helpers.ts +307 -0
- package/src/services/plan/plan-executor-persistence.ts +209 -0
- package/src/services/plan/plan-executor.service.ts +1737 -0
- package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
- package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
- package/src/services/plan/plan-run-serialization.ts +15 -0
- package/src/services/plan/plan-run.service.ts +637 -0
- package/src/services/plan/plan-scheduler.service.ts +379 -0
- package/src/services/plan/plan-template.service.ts +224 -0
- package/src/services/plan/plan-transaction-events.ts +36 -0
- package/src/services/plan/plan-validator.service.ts +907 -0
- package/src/services/plan/plan-workspace.service.ts +131 -0
- package/src/services/plugin-executor.service.ts +102 -68
- package/src/services/quality-metrics.service.ts +112 -94
- package/src/services/queue-job.service.ts +288 -231
- package/src/services/recent-activity-title.service.ts +73 -36
- package/src/services/recent-activity.service.ts +274 -259
- package/src/services/skill-resolver.service.ts +38 -12
- package/src/services/social-chat-history.service.ts +190 -122
- package/src/services/system-executor.service.ts +96 -61
- package/src/services/thread/thread-active-run.ts +203 -0
- package/src/services/thread/thread-bootstrap.ts +385 -0
- package/src/services/thread/thread-listing.ts +199 -0
- package/src/services/thread/thread-memory-block.ts +130 -0
- package/src/services/thread/thread-message.service.ts +379 -0
- package/src/services/thread/thread-record-store.ts +155 -0
- package/src/services/thread/thread-title.service.ts +74 -0
- package/src/services/thread/thread-turn-execution.ts +280 -0
- package/src/services/thread/thread-turn-message-context.ts +73 -0
- package/src/services/thread/thread-turn-preparation.service.ts +1148 -0
- package/src/services/thread/thread-turn-streaming.ts +403 -0
- package/src/services/thread/thread-turn-tracing.ts +35 -0
- package/src/services/thread/thread-turn.ts +376 -0
- package/src/services/thread/thread.service.ts +344 -0
- package/src/services/user.service.ts +82 -32
- package/src/services/write-intent-validator.service.ts +63 -51
- package/src/storage/attachment-parser.ts +69 -27
- package/src/storage/attachment-storage.service.ts +334 -275
- package/src/storage/generated-document-storage.service.ts +66 -34
- package/src/system-agents/agent-result.ts +3 -1
- package/src/system-agents/context-compaction.agent.ts +3 -3
- package/src/system-agents/delegated-agent-factory.ts +159 -90
- package/src/system-agents/helper-agent-options.ts +1 -1
- package/src/system-agents/memory-reranker.agent.ts +3 -3
- package/src/system-agents/memory.agent.ts +3 -3
- package/src/system-agents/recent-activity-title-refiner.agent.ts +3 -3
- package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -3
- package/src/system-agents/skill-extractor.agent.ts +3 -3
- package/src/system-agents/skill-manager.agent.ts +3 -3
- package/src/system-agents/thread-router.agent.ts +157 -113
- package/src/system-agents/title-generator.agent.ts +3 -3
- package/src/tools/execution-plan.tool.ts +241 -171
- package/src/tools/fetch-webpage.tool.ts +29 -18
- package/src/tools/firecrawl-client.ts +26 -6
- package/src/tools/index.ts +1 -0
- package/src/tools/memory-block.tool.ts +14 -6
- package/src/tools/plan-approval.tool.ts +57 -47
- package/src/tools/read-file-parts.tool.ts +44 -33
- package/src/tools/remember-memory.tool.ts +65 -45
- package/src/tools/search-web.tool.ts +33 -22
- package/src/tools/search.tool.ts +41 -29
- package/src/tools/team-think.tool.ts +125 -84
- package/src/tools/user-questions.tool.ts +4 -3
- package/src/tools/web-tool-shared.ts +6 -0
- package/src/utils/async.ts +25 -22
- package/src/utils/crypto.ts +21 -0
- package/src/utils/date-time.ts +40 -1
- package/src/utils/errors.ts +111 -20
- package/src/utils/hono-error-handler.ts +24 -39
- package/src/utils/index.ts +2 -1
- package/src/utils/null-proto-record.ts +41 -0
- package/src/utils/sse-keepalive.ts +124 -21
- package/src/workers/bootstrap.ts +164 -52
- package/src/workers/memory-consolidation.worker.ts +325 -237
- package/src/workers/organization-learning.worker.ts +50 -16
- package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
- package/src/workers/regular-chat-memory-digest.runner.ts +185 -114
- package/src/workers/skill-extraction.runner.ts +176 -93
- package/src/workers/utils/file-section-chunker.ts +8 -10
- package/src/workers/utils/repo-structure-extractor.ts +349 -260
- package/src/workers/utils/repomix-file-sections.ts +2 -2
- package/src/workers/utils/thread-message-query.ts +97 -38
- package/src/workers/worker-utils.ts +74 -31
- package/src/config/debug-logger.ts +0 -47
- package/src/config/search.ts +0 -3
- package/src/redis/connection-accessor.ts +0 -26
- package/src/runtime/agent-types.ts +0 -1
- package/src/runtime/context-compaction-runtime.ts +0 -87
- package/src/runtime/memory-scope.ts +0 -43
- package/src/runtime/social-chat-agent-runner.ts +0 -118
- package/src/runtime/social-chat.ts +0 -516
- package/src/runtime/team-consultation-orchestrator.ts +0 -272
- package/src/services/adaptive-playbook.service.ts +0 -152
- package/src/services/artifact-provenance.service.ts +0 -172
- package/src/services/chat-attachments.service.ts +0 -17
- package/src/services/context-compaction-runtime.singleton.ts +0 -13
- package/src/services/execution-plan.service.ts +0 -1118
- package/src/services/memory.service.ts +0 -914
- package/src/services/plan-agent-heartbeat.service.ts +0 -136
- package/src/services/plan-agent-query.service.ts +0 -267
- package/src/services/plan-artifact.service.ts +0 -50
- package/src/services/plan-builder.service.ts +0 -67
- package/src/services/plan-checkpoint.service.ts +0 -81
- package/src/services/plan-completion-side-effects.ts +0 -80
- package/src/services/plan-coordination.service.ts +0 -157
- package/src/services/plan-cycle.service.ts +0 -284
- package/src/services/plan-deadline.service.ts +0 -430
- package/src/services/plan-event-delivery.service.ts +0 -166
- package/src/services/plan-executor.service.ts +0 -1950
- package/src/services/plan-run.service.ts +0 -515
- package/src/services/plan-scheduler.service.ts +0 -240
- package/src/services/plan-template.service.ts +0 -177
- package/src/services/plan-validator.service.ts +0 -818
- package/src/services/plan-workspace.service.ts +0 -83
- package/src/services/rerank.service.ts +0 -156
- package/src/services/thread-message.service.ts +0 -275
- package/src/services/thread-plan-registry.service.ts +0 -22
- package/src/services/thread-title.service.ts +0 -39
- package/src/services/thread-turn-preparation.service.ts +0 -1147
- package/src/services/thread-turn.ts +0 -172
- package/src/services/thread.service.ts +0 -869
- package/src/utils/env.ts +0 -8
- /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
- /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
- /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
- /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
- /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
- /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
- /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
- /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
|
@@ -4,17 +4,21 @@ import type {
|
|
|
4
4
|
ArtifactReference,
|
|
5
5
|
ArtifactStatus,
|
|
6
6
|
ArtifactVersionSummary,
|
|
7
|
-
GetArtifactResult,
|
|
8
7
|
PublishArtifactArgs,
|
|
9
8
|
} from '@lota-sdk/shared'
|
|
9
|
+
import { Cause, Context, Effect, Layer } from 'effect'
|
|
10
10
|
import { BoundQuery } from 'surrealdb'
|
|
11
11
|
|
|
12
12
|
import type { RecordIdInput } from '../db/record-id'
|
|
13
13
|
import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
14
|
-
import {
|
|
15
|
-
import type { DatabaseTransaction } from '../db/service'
|
|
14
|
+
import type { SurrealDBService, DatabaseTransaction } from '../db/service'
|
|
16
15
|
import { TABLES } from '../db/tables'
|
|
17
|
-
import {
|
|
16
|
+
import { ConfigurationError, DatabaseError, NotFoundError } from '../effect/errors'
|
|
17
|
+
import { DatabaseServiceTag } from '../effect/services'
|
|
18
|
+
import { toValidationError, zodParse } from '../effect/zod'
|
|
19
|
+
import type { makeGeneratedDocumentStorageService } from '../storage/generated-document-storage.service'
|
|
20
|
+
import { GeneratedDocumentStorageServiceTag } from '../storage/generated-document-storage.service'
|
|
21
|
+
import { sha256Hex } from '../utils/crypto'
|
|
18
22
|
import { toIsoDateTimeString } from '../utils/date-time'
|
|
19
23
|
|
|
20
24
|
const ARTIFACT_PUBLISH_MAX_ATTEMPTS = 5
|
|
@@ -48,14 +52,6 @@ function describePublishInputShape(value: unknown): string {
|
|
|
48
52
|
return typeof value
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
function formatPublishError(error: unknown): string {
|
|
52
|
-
if (error instanceof Error && error.message.trim()) {
|
|
53
|
-
return error.message
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return typeof error === 'string' ? error : JSON.stringify(error)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
55
|
function slugify(value: string): string {
|
|
60
56
|
const normalized = value
|
|
61
57
|
.trim()
|
|
@@ -70,7 +66,7 @@ function encodeStorageSegment(value: string): string {
|
|
|
70
66
|
}
|
|
71
67
|
|
|
72
68
|
function hashCanonicalSegment(value: string): string {
|
|
73
|
-
return
|
|
69
|
+
return sha256Hex(value.trim())
|
|
74
70
|
}
|
|
75
71
|
|
|
76
72
|
function buildCanonicalKey(
|
|
@@ -160,61 +156,69 @@ function isRetryablePublishError(error: unknown): boolean {
|
|
|
160
156
|
|
|
161
157
|
interface PublishArtifactTransactionOptions {
|
|
162
158
|
onStorageWrite?: (storageKey: string) => void
|
|
163
|
-
onStorageCleanup?: (storageKey: string) => void
|
|
164
159
|
}
|
|
165
160
|
|
|
166
|
-
|
|
167
|
-
|
|
161
|
+
interface ArtifactServiceDeps {
|
|
162
|
+
db: SurrealDBService
|
|
163
|
+
storage: ReturnType<typeof makeGeneratedDocumentStorageService>
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function makeArtifactService(deps: ArtifactServiceDeps) {
|
|
167
|
+
const { db, storage } = deps
|
|
168
|
+
|
|
169
|
+
const listArtifactsForCanonicalKeyInTransactionEffect = (params: {
|
|
168
170
|
tx: DatabaseTransaction
|
|
169
171
|
organizationId: string
|
|
170
172
|
canonicalKey: string
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
173
|
+
}) =>
|
|
174
|
+
Effect.gen(function* () {
|
|
175
|
+
const records = yield* params.tx.query({
|
|
176
|
+
query: `SELECT * FROM ${TABLES.ARTIFACT} WHERE organizationId = $organizationId AND canonicalKey = $canonicalKey ORDER BY version DESC`,
|
|
177
|
+
bindings: {
|
|
178
|
+
organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
|
|
179
|
+
canonicalKey: params.canonicalKey,
|
|
180
|
+
},
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
return yield* zodParse(ArtifactRecordSchema.array(), records)
|
|
178
184
|
})
|
|
179
185
|
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
async publishArtifactInTransaction(
|
|
186
|
+
const publishArtifactInTransactionEffect = (
|
|
184
187
|
args: PublishArtifactArgs,
|
|
185
188
|
tx: DatabaseTransaction,
|
|
186
189
|
options: PublishArtifactTransactionOptions = {},
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const organizationRef = ensureRecordId(params.organizationId, TABLES.ORGANIZATION)
|
|
197
|
-
const canonicalKey = buildCanonicalKey(params)
|
|
198
|
-
const existing = await this.listArtifactsForCanonicalKeyInTransaction({
|
|
199
|
-
tx,
|
|
200
|
-
organizationId: params.organizationId,
|
|
201
|
-
canonicalKey,
|
|
202
|
-
})
|
|
203
|
-
const latestVersion = existing.reduce((max, record) => Math.max(max, record.version), 0)
|
|
204
|
-
const version = latestVersion + 1
|
|
205
|
-
const publishAttemptId = Bun.randomUUIDv7()
|
|
206
|
-
const references = extractInternalReferences(params.content)
|
|
207
|
-
const stored = await generatedDocumentStorageService.writeTextArtifact({
|
|
208
|
-
organizationId: params.organizationId,
|
|
209
|
-
namespace: 'artifacts',
|
|
210
|
-
relativePath: buildArtifactRelativePath({ canonicalKey, version, title: params.title, publishAttemptId }),
|
|
211
|
-
content: params.content,
|
|
212
|
-
mediaType: 'text/markdown',
|
|
213
|
-
})
|
|
214
|
-
options.onStorageWrite?.(stored.storageKey)
|
|
190
|
+
) =>
|
|
191
|
+
Effect.gen(function* () {
|
|
192
|
+
const parsedArgs = PublishArtifactArgsSchema.safeParse(args)
|
|
193
|
+
if (!parsedArgs.success) {
|
|
194
|
+
return yield* toValidationError(
|
|
195
|
+
parsedArgs.error,
|
|
196
|
+
`artifact service transaction input parse failed (${describePublishInputShape(args)})`,
|
|
197
|
+
)
|
|
198
|
+
}
|
|
215
199
|
|
|
216
|
-
|
|
217
|
-
const
|
|
200
|
+
const params = parsedArgs.data
|
|
201
|
+
const organizationRef = ensureRecordId(params.organizationId, TABLES.ORGANIZATION)
|
|
202
|
+
const canonicalKey = buildCanonicalKey(params)
|
|
203
|
+
const existing = yield* listArtifactsForCanonicalKeyInTransactionEffect({
|
|
204
|
+
tx,
|
|
205
|
+
organizationId: params.organizationId,
|
|
206
|
+
canonicalKey,
|
|
207
|
+
})
|
|
208
|
+
const latestVersion = existing.reduce((max, record) => Math.max(max, record.version), 0)
|
|
209
|
+
const version = latestVersion + 1
|
|
210
|
+
const publishAttemptId = Bun.randomUUIDv7()
|
|
211
|
+
const references = extractInternalReferences(params.content)
|
|
212
|
+
const stored = yield* storage.writeTextArtifact({
|
|
213
|
+
organizationId: params.organizationId,
|
|
214
|
+
namespace: 'artifacts',
|
|
215
|
+
relativePath: buildArtifactRelativePath({ canonicalKey, version, title: params.title, publishAttemptId }),
|
|
216
|
+
content: params.content,
|
|
217
|
+
mediaType: 'text/markdown',
|
|
218
|
+
})
|
|
219
|
+
options.onStorageWrite?.(stored.storageKey)
|
|
220
|
+
|
|
221
|
+
const createdResult = yield* tx
|
|
218
222
|
.create(TABLES.ARTIFACT)
|
|
219
223
|
.content({
|
|
220
224
|
organizationId: organizationRef,
|
|
@@ -238,138 +242,216 @@ class ArtifactService {
|
|
|
238
242
|
.output('after')
|
|
239
243
|
const createdRecord: unknown = Array.isArray(createdResult) ? createdResult.at(0) : createdResult
|
|
240
244
|
if (!createdRecord) {
|
|
241
|
-
|
|
245
|
+
return yield* new ConfigurationError({
|
|
246
|
+
message: `Artifact create returned no record for canonical key ${canonicalKey}.`,
|
|
247
|
+
})
|
|
242
248
|
}
|
|
243
249
|
|
|
244
|
-
const created = ArtifactRecordSchema
|
|
250
|
+
const created = yield* zodParse(ArtifactRecordSchema, createdRecord)
|
|
245
251
|
|
|
246
252
|
const previousActive = existing.find((record) => record.status === 'active')
|
|
247
253
|
if (previousActive) {
|
|
248
|
-
|
|
254
|
+
yield* tx
|
|
249
255
|
.update(ensureRecordId(previousActive.id, TABLES.ARTIFACT))
|
|
250
256
|
.merge({ status: 'superseded', supersededBy: ensureRecordId(created.id, TABLES.ARTIFACT) })
|
|
251
257
|
.output('after')
|
|
252
258
|
}
|
|
253
259
|
|
|
254
260
|
return created
|
|
255
|
-
}
|
|
256
|
-
await generatedDocumentStorageService.deleteTextArtifact(stored.storageKey).catch(() => undefined)
|
|
257
|
-
options.onStorageCleanup?.(stored.storageKey)
|
|
258
|
-
throw error
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
+
})
|
|
261
262
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
263
|
+
const publishArtifactEffect = (args: PublishArtifactArgs) =>
|
|
264
|
+
Effect.gen(function* () {
|
|
265
|
+
const parsedArgs = PublishArtifactArgsSchema.safeParse(args)
|
|
266
|
+
if (!parsedArgs.success) {
|
|
267
|
+
return yield* toValidationError(
|
|
268
|
+
parsedArgs.error,
|
|
269
|
+
`artifact service input parse failed (${describePublishInputShape(args)})`,
|
|
270
|
+
)
|
|
271
|
+
}
|
|
271
272
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
273
|
+
const params = parsedArgs.data
|
|
274
|
+
let lastError: unknown = null
|
|
275
|
+
for (let attempt = 1; attempt <= ARTIFACT_PUBLISH_MAX_ATTEMPTS; attempt += 1) {
|
|
276
|
+
const publishAttemptState: { pendingStorageKey?: string } = {}
|
|
277
|
+
const attemptExit = yield* Effect.exit(
|
|
278
|
+
db.withTransaction((tx) =>
|
|
279
|
+
publishArtifactInTransactionEffect(params, tx, {
|
|
279
280
|
onStorageWrite: (storageKey) => {
|
|
280
281
|
publishAttemptState.pendingStorageKey = storageKey
|
|
281
282
|
},
|
|
282
|
-
onStorageCleanup: (storageKey) => {
|
|
283
|
-
if (publishAttemptState.pendingStorageKey === storageKey) {
|
|
284
|
-
publishAttemptState.pendingStorageKey = undefined
|
|
285
|
-
}
|
|
286
|
-
},
|
|
287
283
|
}),
|
|
284
|
+
),
|
|
288
285
|
)
|
|
289
|
-
|
|
286
|
+
if (attemptExit._tag === 'Success') {
|
|
287
|
+
return attemptExit.value
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const error = Cause.squash(attemptExit.cause)
|
|
290
291
|
const storageKeyToCleanup = publishAttemptState.pendingStorageKey
|
|
291
292
|
publishAttemptState.pendingStorageKey = undefined
|
|
292
293
|
if (typeof storageKeyToCleanup === 'string') {
|
|
293
|
-
|
|
294
|
+
yield* Effect.ignore(storage.deleteTextArtifact(storageKeyToCleanup))
|
|
294
295
|
}
|
|
295
296
|
lastError = error
|
|
296
297
|
if (!isRetryablePublishError(error) || attempt === ARTIFACT_PUBLISH_MAX_ATTEMPTS) {
|
|
297
|
-
|
|
298
|
+
if (error instanceof Error) {
|
|
299
|
+
return yield* Effect.fail(error)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return yield* new ConfigurationError({ message: 'Artifact publish failed.' })
|
|
298
303
|
}
|
|
299
304
|
}
|
|
300
|
-
}
|
|
301
305
|
|
|
302
|
-
|
|
303
|
-
|
|
306
|
+
if (lastError instanceof Error) {
|
|
307
|
+
return yield* Effect.fail(lastError)
|
|
308
|
+
}
|
|
304
309
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
310
|
+
return yield* new ConfigurationError({ message: 'Artifact publish failed.' })
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
const getArtifactRecordEffect = (artifactId: RecordIdInput) =>
|
|
314
|
+
db
|
|
315
|
+
.findOne(TABLES.ARTIFACT, { id: ensureRecordId(artifactId, TABLES.ARTIFACT) }, ArtifactRecordSchema)
|
|
316
|
+
.pipe(
|
|
317
|
+
Effect.mapError(
|
|
318
|
+
(cause) =>
|
|
319
|
+
new DatabaseError({
|
|
320
|
+
message: `Failed to load artifact ${recordIdToString(artifactId, TABLES.ARTIFACT)}.`,
|
|
321
|
+
cause,
|
|
322
|
+
}),
|
|
323
|
+
),
|
|
324
|
+
)
|
|
312
325
|
|
|
313
|
-
|
|
326
|
+
const listArtifactsEffect = (params: {
|
|
314
327
|
organizationId: RecordIdInput
|
|
315
328
|
canonicalKey?: string
|
|
316
329
|
status?: ArtifactStatus
|
|
317
330
|
includeNonActive?: boolean
|
|
318
331
|
sourceThreadId?: RecordIdInput
|
|
319
332
|
limit?: number
|
|
320
|
-
})
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
333
|
+
}) =>
|
|
334
|
+
Effect.gen(function* () {
|
|
335
|
+
const records = yield* db
|
|
336
|
+
.findMany(
|
|
337
|
+
TABLES.ARTIFACT,
|
|
338
|
+
{
|
|
339
|
+
organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
|
|
340
|
+
...(params.canonicalKey ? { canonicalKey: params.canonicalKey } : {}),
|
|
341
|
+
...(params.sourceThreadId ? { sourceThreadId: ensureRecordId(params.sourceThreadId, TABLES.THREAD) } : {}),
|
|
342
|
+
},
|
|
343
|
+
ArtifactRecordSchema,
|
|
344
|
+
{ orderBy: 'createdAt', orderDir: 'DESC', ...(params.limit ? { limit: params.limit } : {}) },
|
|
345
|
+
)
|
|
346
|
+
.pipe(
|
|
347
|
+
Effect.mapError(
|
|
348
|
+
(cause) =>
|
|
349
|
+
new DatabaseError({
|
|
350
|
+
message: `Failed to list artifacts for organization ${recordIdToString(params.organizationId, TABLES.ORGANIZATION)}.`,
|
|
351
|
+
cause,
|
|
352
|
+
}),
|
|
353
|
+
),
|
|
354
|
+
)
|
|
335
355
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
356
|
+
if (params.status) {
|
|
357
|
+
return records.filter((record) => record.status === params.status)
|
|
358
|
+
}
|
|
339
359
|
|
|
340
|
-
|
|
341
|
-
|
|
360
|
+
if (params.includeNonActive) {
|
|
361
|
+
return records
|
|
362
|
+
}
|
|
342
363
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if (!artifact) {
|
|
346
|
-
throw new Error(`Artifact not found: ${recordIdToString(artifactId, TABLES.ARTIFACT)}`)
|
|
347
|
-
}
|
|
364
|
+
return records.filter((record) => record.status === 'active')
|
|
365
|
+
})
|
|
348
366
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
return GetArtifactResultSchema.parse({ artifact, content, versions: versions.map(toVersionSummary), backlinks })
|
|
360
|
-
}
|
|
367
|
+
const getArtifactEffect = (artifactId: RecordIdInput) =>
|
|
368
|
+
Effect.gen(function* () {
|
|
369
|
+
const artifact = yield* getArtifactRecordEffect(artifactId)
|
|
370
|
+
if (!artifact) {
|
|
371
|
+
return yield* new NotFoundError({
|
|
372
|
+
resource: TABLES.ARTIFACT,
|
|
373
|
+
id: recordIdToString(artifactId, TABLES.ARTIFACT),
|
|
374
|
+
message: `Artifact not found: ${recordIdToString(artifactId, TABLES.ARTIFACT)}`,
|
|
375
|
+
})
|
|
376
|
+
}
|
|
361
377
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
378
|
+
const [content, versions, backlinks] = yield* Effect.all([
|
|
379
|
+
storage
|
|
380
|
+
.readTextArtifact(artifact.storageKey)
|
|
381
|
+
.pipe(
|
|
382
|
+
Effect.mapError(
|
|
383
|
+
(cause) =>
|
|
384
|
+
new DatabaseError({ message: `Failed to read artifact content for ${artifact.canonicalKey}.`, cause }),
|
|
385
|
+
),
|
|
386
|
+
),
|
|
387
|
+
listArtifactsEffect({
|
|
388
|
+
organizationId: artifact.organizationId,
|
|
389
|
+
canonicalKey: artifact.canonicalKey,
|
|
390
|
+
includeNonActive: true,
|
|
391
|
+
}),
|
|
392
|
+
listBacklinksEffect(artifact),
|
|
393
|
+
])
|
|
394
|
+
|
|
395
|
+
return yield* zodParse(GetArtifactResultSchema, {
|
|
396
|
+
artifact,
|
|
397
|
+
content,
|
|
398
|
+
versions: versions.map(toVersionSummary),
|
|
399
|
+
backlinks,
|
|
400
|
+
})
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
const listBacklinksEffect = (artifact: Pick<ArtifactRecord, 'id' | 'organizationId'>) =>
|
|
404
|
+
db
|
|
405
|
+
.queryMany(
|
|
406
|
+
new BoundQuery(
|
|
407
|
+
`SELECT * FROM ${TABLES.ARTIFACT} WHERE organizationId = $organizationId AND references[*].targetId CONTAINS $targetId`,
|
|
408
|
+
{
|
|
409
|
+
organizationId: ensureRecordId(artifact.organizationId, TABLES.ORGANIZATION),
|
|
410
|
+
targetId: recordIdToString(artifact.id, TABLES.ARTIFACT),
|
|
411
|
+
},
|
|
412
|
+
),
|
|
413
|
+
ArtifactRecordSchema,
|
|
414
|
+
)
|
|
415
|
+
.pipe(
|
|
416
|
+
Effect.mapError(
|
|
417
|
+
(cause) =>
|
|
418
|
+
new DatabaseError({
|
|
419
|
+
message: `Failed to load backlinks for ${recordIdToString(artifact.id, TABLES.ARTIFACT)}.`,
|
|
420
|
+
cause,
|
|
421
|
+
}),
|
|
422
|
+
),
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
return {
|
|
426
|
+
publishArtifactInTransaction: (
|
|
427
|
+
params: PublishArtifactArgs,
|
|
428
|
+
tx: DatabaseTransaction,
|
|
429
|
+
options?: PublishArtifactTransactionOptions,
|
|
430
|
+
) => publishArtifactInTransactionEffect(params, tx, options),
|
|
431
|
+
publishArtifact: (params: PublishArtifactArgs) => publishArtifactEffect(params),
|
|
432
|
+
getArtifactRecord: (artifactId: RecordIdInput) => getArtifactRecordEffect(artifactId),
|
|
433
|
+
listArtifacts: (params: {
|
|
434
|
+
organizationId: RecordIdInput
|
|
435
|
+
canonicalKey?: string
|
|
436
|
+
status?: ArtifactStatus
|
|
437
|
+
includeNonActive?: boolean
|
|
438
|
+
sourceThreadId?: RecordIdInput
|
|
439
|
+
limit?: number
|
|
440
|
+
}) => listArtifactsEffect(params),
|
|
441
|
+
getArtifact: (artifactId: RecordIdInput) => getArtifactEffect(artifactId),
|
|
442
|
+
listBacklinks: (artifact: Pick<ArtifactRecord, 'id' | 'organizationId'>) => listBacklinksEffect(artifact),
|
|
372
443
|
}
|
|
373
444
|
}
|
|
374
445
|
|
|
375
|
-
export
|
|
446
|
+
export class ArtifactServiceTag extends Context.Service<ArtifactServiceTag, ReturnType<typeof makeArtifactService>>()(
|
|
447
|
+
'@lota-sdk/core/ArtifactService',
|
|
448
|
+
) {}
|
|
449
|
+
|
|
450
|
+
export const ArtifactServiceLive = Layer.effect(
|
|
451
|
+
ArtifactServiceTag,
|
|
452
|
+
Effect.gen(function* () {
|
|
453
|
+
const db = yield* DatabaseServiceTag
|
|
454
|
+
const storage = yield* GeneratedDocumentStorageServiceTag
|
|
455
|
+
return makeArtifactService({ db, storage })
|
|
456
|
+
}),
|
|
457
|
+
)
|