@lota-sdk/core 0.1.9 → 0.1.12
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/infrastructure/schema/00_workstream.surql +1 -0
- package/infrastructure/schema/02_execution_plan.surql +202 -52
- package/package.json +4 -87
- package/src/ai/index.ts +3 -0
- package/src/bifrost/bifrost.ts +94 -25
- package/src/bifrost/index.ts +1 -0
- package/src/config/agent-defaults.ts +30 -7
- package/src/config/constants.ts +0 -9
- package/src/config/debug-logger.ts +43 -0
- package/src/config/index.ts +5 -0
- package/src/config/model-constants.ts +8 -9
- package/src/config/workstream-defaults.ts +4 -0
- package/src/db/cursor-pagination.ts +2 -2
- package/src/db/index.ts +10 -0
- package/src/db/memory-store.ts +3 -71
- package/src/db/memory.ts +9 -15
- package/src/db/service.ts +42 -2
- package/src/db/tables.ts +9 -2
- package/src/document/index.ts +2 -0
- package/src/document/parsing.ts +0 -25
- package/src/embeddings/provider.ts +102 -22
- package/src/index.ts +15 -499
- package/src/queues/index.ts +10 -0
- package/src/redis/connection-accessor.ts +26 -0
- package/src/redis/connection.ts +1 -1
- package/src/redis/index.ts +9 -25
- package/src/redis/org-memory-lock.ts +1 -1
- package/src/redis/redis-lease-lock.ts +1 -1
- package/src/redis/stream-context.ts +54 -0
- package/src/runtime/agent-runtime-policy.ts +9 -5
- package/src/runtime/agent-stream-helpers.ts +6 -3
- package/src/runtime/agent-types.ts +1 -5
- package/src/runtime/approval-continuation.ts +68 -1
- package/src/runtime/chat-attachments.ts +1 -1
- package/src/runtime/chat-request-routing.ts +6 -2
- package/src/runtime/context-compaction-runtime.ts +2 -2
- package/src/runtime/context-compaction.ts +1 -1
- package/src/runtime/execution-plan.ts +22 -15
- package/src/runtime/index.ts +26 -0
- package/src/runtime/indexed-repositories-policy.ts +10 -10
- package/src/runtime/memory-pipeline.ts +0 -2
- package/src/runtime/runtime-config.ts +238 -0
- package/src/runtime/runtime-extensions.ts +3 -2
- package/src/runtime/runtime-worker-registry.ts +47 -0
- package/src/runtime/team-consultation-orchestrator.ts +9 -6
- package/src/runtime/team-consultation-prompts.ts +3 -2
- package/src/runtime/turn-lifecycle.ts +13 -5
- package/src/runtime/workstream-chat-helpers.ts +0 -54
- package/src/runtime/workstream-routing-policy.ts +3 -7
- package/src/runtime.ts +387 -0
- package/src/services/chat-attachments.service.ts +1 -1
- package/src/services/context-compaction.service.ts +1 -1
- package/src/services/document-chunk.service.ts +2 -2
- package/src/services/execution-plan.service.ts +584 -793
- package/src/services/index.ts +14 -0
- package/src/services/learned-skill.service.ts +82 -39
- package/src/services/memory.service.ts +5 -4
- package/src/services/mutating-approval.service.ts +1 -1
- package/src/services/organization-member.service.ts +1 -1
- package/src/services/organization.service.ts +1 -1
- package/src/services/plan-approval.service.ts +83 -0
- package/src/services/plan-artifact.service.ts +44 -0
- package/src/services/plan-builder.service.ts +61 -0
- package/src/services/plan-checkpoint.service.ts +53 -0
- package/src/services/plan-compiler.service.ts +81 -0
- package/src/services/plan-executor.service.ts +1624 -0
- package/src/services/plan-run.service.ts +422 -0
- package/src/services/plan-validator.service.ts +760 -0
- package/src/services/recent-activity-title.service.ts +1 -1
- package/src/services/recent-activity.service.ts +14 -16
- package/src/services/user.service.ts +2 -2
- package/src/services/workstream-message.service.ts +2 -3
- package/src/services/workstream-title.service.ts +1 -1
- package/src/services/workstream-turn-preparation.ts +156 -59
- package/src/services/workstream-turn.ts +26 -1
- package/src/services/workstream.service.ts +35 -9
- package/src/services/workstream.types.ts +1 -0
- package/src/storage/attachment-parser.ts +1 -1
- package/src/storage/attachment-storage.service.ts +11 -10
- package/src/storage/generated-document-storage.service.ts +7 -6
- package/src/storage/index.ts +10 -0
- package/src/system-agents/delegated-agent-factory.ts +78 -29
- package/src/system-agents/index.ts +4 -0
- package/src/system-agents/recent-activity-title-refiner.agent.ts +38 -3
- package/src/system-agents/regular-chat-memory-digest.agent.ts +1 -1
- package/src/system-agents/skill-extractor.agent.ts +1 -1
- package/src/system-agents/skill-manager.agent.ts +2 -4
- package/src/system-agents/title-generator.agent.ts +2 -2
- package/src/tools/execution-plan.tool.ts +22 -48
- package/src/tools/firecrawl-client.ts +2 -2
- package/src/tools/index.ts +12 -0
- package/src/tools/log-hello-world.tool.ts +17 -0
- package/src/tools/research-topic.tool.ts +1 -1
- package/src/tools/team-think.tool.ts +1 -1
- package/src/tools/user-questions.tool.ts +2 -2
- package/src/utils/index.ts +6 -0
- package/src/workers/bootstrap.ts +8 -16
- package/src/workers/index.ts +7 -0
- package/src/workers/regular-chat-memory-digest.runner.ts +1 -1
- package/src/workers/skill-extraction.runner.ts +3 -3
- package/src/workers/utils/{repo-indexer-chunker.ts → file-section-chunker.ts} +23 -52
- package/src/workers/utils/repo-structure-extractor.ts +2 -5
- package/src/workers/utils/repomix-file-sections.ts +42 -0
- package/src/config/env-shapes.ts +0 -121
- package/src/runtime/agent-contract.ts +0 -1
|
@@ -12,7 +12,7 @@ export const researchTopicTool = createDelegatedAgentTool({
|
|
|
12
12
|
id: 'researchTopic',
|
|
13
13
|
description:
|
|
14
14
|
'Delegate a research task to a dedicated research agent that searches the web, fetches pages, and returns a synthesized markdown report. Call multiple instances in parallel for broad research across different topics.',
|
|
15
|
-
model: bifrostChatModel(OPENROUTER_WEB_RESEARCH_MODEL_ID),
|
|
15
|
+
model: () => bifrostChatModel(OPENROUTER_WEB_RESEARCH_MODEL_ID),
|
|
16
16
|
providerOptions: OPENROUTER_MEDIUM_REASONING_PROVIDER_OPTIONS,
|
|
17
17
|
instructions: RESEARCHER_PROMPT,
|
|
18
18
|
tools: { searchWeb: searchWebTool.create(), fetchWebpage: fetchWebpageTool.create() },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { USER_QUESTIONS_TOOL_NAME, UserQuestionsArgsSchema } from '@lota-sdk/shared
|
|
2
|
-
import type { UserQuestionsArgs } from '@lota-sdk/shared
|
|
1
|
+
import { USER_QUESTIONS_TOOL_NAME, UserQuestionsArgsSchema } from '@lota-sdk/shared'
|
|
2
|
+
import type { UserQuestionsArgs } from '@lota-sdk/shared'
|
|
3
3
|
import { tool } from 'ai'
|
|
4
4
|
|
|
5
5
|
import type { ToolDefinition } from '../ai/definitions'
|
package/src/workers/bootstrap.ts
CHANGED
|
@@ -2,26 +2,17 @@ import { configureLogger, serverLogger } from '../config/logger'
|
|
|
2
2
|
import { LOTA_SDK_DATABASE_NAME } from '../db/sdk-database'
|
|
3
3
|
import { SurrealDBService, databaseService, setDatabaseService } from '../db/service'
|
|
4
4
|
import { connectWithStartupRetry, waitForDatabaseBootstrap } from '../db/startup'
|
|
5
|
+
import { parseWorkerBootstrapEnv } from '../runtime/runtime-config'
|
|
5
6
|
import { getConfiguredPluginDatabaseConnector } from '../runtime/runtime-extensions'
|
|
6
7
|
|
|
7
|
-
/**
|
|
8
|
-
* Sandboxed BullMQ workers run in separate child processes where createLotaRuntime
|
|
9
|
-
* was never called, so the global databaseService proxy has no backing instance.
|
|
10
|
-
* Create one from env vars so the proxy resolves.
|
|
11
|
-
*/
|
|
12
|
-
function getRequiredEnv(key: string): string {
|
|
13
|
-
const value = process.env[key]
|
|
14
|
-
if (!value) throw new Error(`Missing required env var: ${key}`)
|
|
15
|
-
return value
|
|
16
|
-
}
|
|
17
|
-
|
|
18
8
|
function ensureDatabaseServiceConfigured(): void {
|
|
9
|
+
const env = parseWorkerBootstrapEnv(process.env)
|
|
19
10
|
const db = new SurrealDBService({
|
|
20
|
-
url:
|
|
21
|
-
namespace:
|
|
11
|
+
url: env.SURREALDB_URL,
|
|
12
|
+
namespace: env.SURREALDB_NAMESPACE,
|
|
22
13
|
database: LOTA_SDK_DATABASE_NAME,
|
|
23
|
-
username:
|
|
24
|
-
password:
|
|
14
|
+
username: env.SURREALDB_USER,
|
|
15
|
+
password: env.SURREALDB_PASSWORD,
|
|
25
16
|
})
|
|
26
17
|
setDatabaseService(db)
|
|
27
18
|
}
|
|
@@ -35,6 +26,7 @@ export async function initializeSandboxedWorkerRuntime(): Promise<void> {
|
|
|
35
26
|
}
|
|
36
27
|
|
|
37
28
|
sandboxedWorkerRuntimePromise = (async () => {
|
|
29
|
+
const env = parseWorkerBootstrapEnv(process.env)
|
|
38
30
|
await configureLogger()
|
|
39
31
|
|
|
40
32
|
ensureDatabaseServiceConfigured()
|
|
@@ -58,7 +50,7 @@ export async function initializeSandboxedWorkerRuntime(): Promise<void> {
|
|
|
58
50
|
|
|
59
51
|
await waitForDatabaseBootstrap({
|
|
60
52
|
databaseService,
|
|
61
|
-
expectedFingerprint:
|
|
53
|
+
expectedFingerprint: env.DB_SCHEMA_FINGERPRINT,
|
|
62
54
|
label: 'sandboxed worker runtime',
|
|
63
55
|
logger: serverLogger,
|
|
64
56
|
connect: () => databaseService.connect(),
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './bootstrap'
|
|
2
|
+
export * from './regular-chat-memory-digest.helpers'
|
|
3
|
+
export * from './worker-utils'
|
|
4
|
+
export * from './utils/file-section-chunker'
|
|
5
|
+
export * from './utils/repomix-file-sections'
|
|
6
|
+
export * from './utils/repo-structure-extractor'
|
|
7
|
+
export * from './utils/repomix-process-concurrency'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { toTimestamp } from '@lota-sdk/shared
|
|
1
|
+
import { toTimestamp } from '@lota-sdk/shared'
|
|
2
2
|
import { BoundQuery } from 'surrealdb'
|
|
3
3
|
import { z } from 'zod'
|
|
4
4
|
|
|
@@ -7,7 +7,7 @@ import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
|
7
7
|
import type { RecordIdRef } from '../db/record-id'
|
|
8
8
|
import { databaseService } from '../db/service'
|
|
9
9
|
import { TABLES } from '../db/tables'
|
|
10
|
-
import {
|
|
10
|
+
import { getDefaultEmbeddings } from '../embeddings/provider'
|
|
11
11
|
import type { SkillExtractionJob } from '../queues/skill-extraction.queue'
|
|
12
12
|
import { createHelperModelRuntime } from '../runtime/helper-model'
|
|
13
13
|
import { getRuntimeAdapters, withConfiguredWorkspaceMemoryLock } from '../runtime/runtime-extensions'
|
|
@@ -54,7 +54,7 @@ interface SkillExtractionRunResult {
|
|
|
54
54
|
extractedSkills: number
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
const embeddings =
|
|
57
|
+
const embeddings = getDefaultEmbeddings()
|
|
58
58
|
|
|
59
59
|
const helperModelRuntime = createHelperModelRuntime()
|
|
60
60
|
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
export const
|
|
2
|
-
const
|
|
3
|
-
export const
|
|
1
|
+
export const DEFAULT_FILE_SECTION_CHUNK_MAX_CHARS = 250_000
|
|
2
|
+
const MIN_FILE_SECTION_CHUNK_MAX_CHARS = 4_000
|
|
3
|
+
export const DEFAULT_FILE_SECTION_CHUNK_MIN_CHARS = 10_000
|
|
4
4
|
const SECTION_SEPARATOR_LENGTH = 2
|
|
5
|
-
const FILE_SECTION_HEADER_SOURCE = '^## File:\\s+(.+)$'
|
|
6
5
|
|
|
7
|
-
interface
|
|
6
|
+
export interface FileSection {
|
|
8
7
|
kind: 'preamble' | 'file'
|
|
9
8
|
content: string
|
|
10
9
|
filePath?: string
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
export interface
|
|
12
|
+
export interface FileSectionChunk {
|
|
14
13
|
index: number
|
|
15
14
|
totalChunks: number
|
|
16
15
|
content: string
|
|
@@ -22,7 +21,7 @@ export interface RepomixContextChunk {
|
|
|
22
21
|
lastFilePath: string | null
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
interface
|
|
24
|
+
export interface FileSectionChunkOptions {
|
|
26
25
|
maxChars?: number
|
|
27
26
|
minChunkChars?: number
|
|
28
27
|
preserveCodeFenceIntegrity?: boolean
|
|
@@ -35,14 +34,14 @@ function estimateTokenCountFromChars(text: string): number {
|
|
|
35
34
|
|
|
36
35
|
function normalizeMaxChars(value?: number): number {
|
|
37
36
|
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
38
|
-
return
|
|
37
|
+
return DEFAULT_FILE_SECTION_CHUNK_MAX_CHARS
|
|
39
38
|
}
|
|
40
|
-
return Math.max(
|
|
39
|
+
return Math.max(MIN_FILE_SECTION_CHUNK_MAX_CHARS, Math.floor(value))
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
function normalizeMinChunkChars(value: number | undefined, maxChars: number): number {
|
|
44
43
|
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
45
|
-
return Math.min(
|
|
44
|
+
return Math.min(DEFAULT_FILE_SECTION_CHUNK_MIN_CHARS, Math.floor(maxChars * 0.35))
|
|
46
45
|
}
|
|
47
46
|
const normalized = Math.max(512, Math.floor(value))
|
|
48
47
|
return Math.min(normalized, Math.floor(maxChars * 0.6))
|
|
@@ -156,9 +155,9 @@ function mergeTinyTailParts(parts: string[], options: { minChunkChars: number; m
|
|
|
156
155
|
}
|
|
157
156
|
|
|
158
157
|
async function splitOversizedSection(
|
|
159
|
-
section:
|
|
158
|
+
section: FileSection,
|
|
160
159
|
options: { maxChars: number; minChunkChars: number; preserveCodeFenceIntegrity: boolean },
|
|
161
|
-
): Promise<
|
|
160
|
+
): Promise<FileSection[]> {
|
|
162
161
|
if (section.content.length <= options.maxChars) {
|
|
163
162
|
return [section]
|
|
164
163
|
}
|
|
@@ -188,43 +187,12 @@ async function splitOversizedSection(
|
|
|
188
187
|
}))
|
|
189
188
|
}
|
|
190
189
|
|
|
191
|
-
function parseRepomixSections(repomixOutput: string): RepomixSection[] {
|
|
192
|
-
const source = repomixOutput.trim()
|
|
193
|
-
if (!source) return []
|
|
194
|
-
|
|
195
|
-
const matches = Array.from(source.matchAll(new RegExp(FILE_SECTION_HEADER_SOURCE, 'gm')))
|
|
196
|
-
if (matches.length === 0) {
|
|
197
|
-
return [{ kind: 'preamble', content: source }]
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const sections: RepomixSection[] = []
|
|
201
|
-
const firstMatch = matches.at(0)
|
|
202
|
-
const firstIndex = firstMatch ? firstMatch.index : 0
|
|
203
|
-
if (firstIndex > 0) {
|
|
204
|
-
const preamble = source.slice(0, firstIndex).trim()
|
|
205
|
-
if (preamble) {
|
|
206
|
-
sections.push({ kind: 'preamble', content: preamble })
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
for (const [index, match] of matches.entries()) {
|
|
211
|
-
const start = match.index
|
|
212
|
-
const nextStart = matches[index + 1]?.index ?? source.length
|
|
213
|
-
const content = source.slice(start, nextStart).trim()
|
|
214
|
-
if (!content) continue
|
|
215
|
-
const filePath = (match[1] ?? '').trim()
|
|
216
|
-
sections.push({ kind: 'file', content, filePath: filePath || undefined })
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return sections
|
|
220
|
-
}
|
|
221
|
-
|
|
222
190
|
function mergeTinyChunks(
|
|
223
|
-
chunks: Omit<
|
|
191
|
+
chunks: Omit<FileSectionChunk, 'index' | 'totalChunks'>[],
|
|
224
192
|
options: { minChunkChars: number; maxChars: number },
|
|
225
|
-
): Omit<
|
|
193
|
+
): Omit<FileSectionChunk, 'index' | 'totalChunks'>[] {
|
|
226
194
|
if (chunks.length <= 1) return chunks
|
|
227
|
-
const merged: Omit<
|
|
195
|
+
const merged: Omit<FileSectionChunk, 'index' | 'totalChunks'>[] = []
|
|
228
196
|
|
|
229
197
|
for (const chunk of chunks) {
|
|
230
198
|
const previous = merged.at(-1)
|
|
@@ -252,15 +220,18 @@ function mergeTinyChunks(
|
|
|
252
220
|
return merged
|
|
253
221
|
}
|
|
254
222
|
|
|
255
|
-
export async function
|
|
256
|
-
|
|
257
|
-
options:
|
|
258
|
-
): Promise<
|
|
223
|
+
export async function chunkFileSections(
|
|
224
|
+
fileSections: readonly FileSection[],
|
|
225
|
+
options: FileSectionChunkOptions = {},
|
|
226
|
+
): Promise<FileSectionChunk[]> {
|
|
259
227
|
const maxChars = normalizeMaxChars(options.maxChars)
|
|
260
228
|
const minChunkChars = normalizeMinChunkChars(options.minChunkChars, maxChars)
|
|
261
229
|
const preserveCodeFenceIntegrity = options.preserveCodeFenceIntegrity ?? true
|
|
262
230
|
|
|
263
|
-
const rawSections =
|
|
231
|
+
const rawSections = fileSections
|
|
232
|
+
.map((section) => ({ kind: section.kind, content: section.content.trim(), filePath: section.filePath }))
|
|
233
|
+
.filter((section) => section.content.length > 0)
|
|
234
|
+
|
|
264
235
|
const splitSections = await Promise.all(
|
|
265
236
|
rawSections.map(
|
|
266
237
|
async (section) => await splitOversizedSection(section, { maxChars, minChunkChars, preserveCodeFenceIntegrity }),
|
|
@@ -270,7 +241,7 @@ export async function chunkRepomixOutput(
|
|
|
270
241
|
|
|
271
242
|
if (sections.length === 0) return []
|
|
272
243
|
|
|
273
|
-
const chunks: Omit<
|
|
244
|
+
const chunks: Omit<FileSectionChunk, 'index' | 'totalChunks'>[] = []
|
|
274
245
|
let currentParts: string[] = []
|
|
275
246
|
let currentCharLength = 0
|
|
276
247
|
let currentSectionCount = 0
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import { readdir, readFile } from 'node:fs/promises'
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
RepositoryStructureArtifactSchema,
|
|
6
|
-
RepositoryStructureSummarySchema,
|
|
7
|
-
} from '@lota-sdk/shared/schemas/repository-structure'
|
|
4
|
+
import { RepositoryStructureArtifactSchema, RepositoryStructureSummarySchema } from '@lota-sdk/shared'
|
|
8
5
|
import type {
|
|
9
6
|
RepositoryStructureArtifact,
|
|
10
7
|
RepositoryStructureComponent,
|
|
11
8
|
RepositoryStructureShape,
|
|
12
9
|
RepositoryStructureSignal,
|
|
13
10
|
RepositoryStructureSummary,
|
|
14
|
-
} from '@lota-sdk/shared
|
|
11
|
+
} from '@lota-sdk/shared'
|
|
15
12
|
|
|
16
13
|
const EXTRACTOR_VERSION = 'repository-structure-extractor.v1'
|
|
17
14
|
const IGNORED_DIR_NAMES = new Set([
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { FileSection, FileSectionChunk, FileSectionChunkOptions } from './file-section-chunker'
|
|
2
|
+
import { chunkFileSections } from './file-section-chunker'
|
|
3
|
+
|
|
4
|
+
const FILE_SECTION_HEADER_SOURCE = '^## File:\\s+(.+)$'
|
|
5
|
+
|
|
6
|
+
export function parseRepomixFileSections(repomixOutput: string): FileSection[] {
|
|
7
|
+
const source = repomixOutput.trim()
|
|
8
|
+
if (!source) return []
|
|
9
|
+
|
|
10
|
+
const matches = Array.from(source.matchAll(new RegExp(FILE_SECTION_HEADER_SOURCE, 'gm')))
|
|
11
|
+
if (matches.length === 0) {
|
|
12
|
+
return [{ kind: 'preamble', content: source }]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const sections: FileSection[] = []
|
|
16
|
+
const firstMatch = matches.at(0)
|
|
17
|
+
const firstIndex = firstMatch ? firstMatch.index : 0
|
|
18
|
+
if (firstIndex > 0) {
|
|
19
|
+
const preamble = source.slice(0, firstIndex).trim()
|
|
20
|
+
if (preamble) {
|
|
21
|
+
sections.push({ kind: 'preamble', content: preamble })
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
for (const [index, match] of matches.entries()) {
|
|
26
|
+
const start = match.index
|
|
27
|
+
const nextStart = matches[index + 1]?.index ?? source.length
|
|
28
|
+
const content = source.slice(start, nextStart).trim()
|
|
29
|
+
if (!content) continue
|
|
30
|
+
const filePath = (match[1] ?? '').trim()
|
|
31
|
+
sections.push({ kind: 'file', content, filePath: filePath || undefined })
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return sections
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function chunkRepomixFileSections(
|
|
38
|
+
repomixOutput: string,
|
|
39
|
+
options: FileSectionChunkOptions = {},
|
|
40
|
+
): Promise<FileSectionChunk[]> {
|
|
41
|
+
return await chunkFileSections(parseRepomixFileSections(repomixOutput), options)
|
|
42
|
+
}
|
package/src/config/env-shapes.ts
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import type { ZodTypeAny } from 'zod'
|
|
2
|
-
import { z } from 'zod'
|
|
3
|
-
|
|
4
|
-
const logLevelValues = ['trace', 'debug', 'info', 'warning', 'error', 'fatal'] as const
|
|
5
|
-
|
|
6
|
-
export const nodeServerEnvShape = {
|
|
7
|
-
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
|
8
|
-
PORT: z.coerce.number().default(3000),
|
|
9
|
-
MAIN_CLIENT_URL: z.string().min(1).default('http://localhost:5173'),
|
|
10
|
-
} as const satisfies Record<string, ZodTypeAny>
|
|
11
|
-
|
|
12
|
-
export const surrealDbEnvShape = {
|
|
13
|
-
SURREALDB_URL: z.string().min(1, 'SurrealDB URL is required'),
|
|
14
|
-
SURREALDB_USER: z.string().min(1, 'SurrealDB user is required'),
|
|
15
|
-
SURREALDB_PASSWORD: z.string().min(1, 'SurrealDB password is required'),
|
|
16
|
-
SURREALDB_NAMESPACE: z.string().min(1, 'SurrealDB namespace is required'),
|
|
17
|
-
} as const satisfies Record<string, ZodTypeAny>
|
|
18
|
-
|
|
19
|
-
export const betterAuthEnvShape = {
|
|
20
|
-
BETTER_AUTH_SECRET: z.string().min(32, 'Better Auth secret must be at least 32 characters long'),
|
|
21
|
-
} as const satisfies Record<string, ZodTypeAny>
|
|
22
|
-
|
|
23
|
-
export const serverUrlEnvShape = { SERVER_URL: z.string().min(1, 'Server URL is required') } as const satisfies Record<
|
|
24
|
-
string,
|
|
25
|
-
ZodTypeAny
|
|
26
|
-
>
|
|
27
|
-
|
|
28
|
-
export const aiGatewayEnvShape = {
|
|
29
|
-
AI_GATEWAY_URL: z.string().url('AI gateway URL is required'),
|
|
30
|
-
AI_GATEWAY_KEY: z.string().min(1, 'AI gateway key is required'),
|
|
31
|
-
AI_GATEWAY_ADMIN: z.string().min(1).optional(),
|
|
32
|
-
AI_GATEWAY_PASS: z.string().min(1).optional(),
|
|
33
|
-
AI_EMBEDDING_MODEL: z.string().default('openai/text-embedding-3-small'),
|
|
34
|
-
} as const satisfies Record<string, ZodTypeAny>
|
|
35
|
-
|
|
36
|
-
export const firecrawlEnvShape = {
|
|
37
|
-
FIRECRAWL_API_KEY: z
|
|
38
|
-
.string()
|
|
39
|
-
.min(1, 'Firecrawl API key is required')
|
|
40
|
-
.refine((value) => value.startsWith('fc-'), 'Firecrawl API key must start with fc-')
|
|
41
|
-
.refine((value) => value !== 'dev-fire-key', 'Firecrawl API key placeholder is not allowed'),
|
|
42
|
-
FIRECRAWL_API_BASE_URL: z.string().url().optional(),
|
|
43
|
-
} as const satisfies Record<string, ZodTypeAny>
|
|
44
|
-
|
|
45
|
-
export const memorySearchEnvShape = {
|
|
46
|
-
MEMORY_SEARCH_K: z.coerce.number().int().positive().default(6),
|
|
47
|
-
} as const satisfies Record<string, ZodTypeAny>
|
|
48
|
-
|
|
49
|
-
export const redisEnvShape = { REDIS_URL: z.string().min(1, 'Redis URL is required') } as const satisfies Record<
|
|
50
|
-
string,
|
|
51
|
-
ZodTypeAny
|
|
52
|
-
>
|
|
53
|
-
|
|
54
|
-
export const s3StorageEnvShape = {
|
|
55
|
-
S3_ENDPOINT: z.string().min(1, 'S3 endpoint is required'),
|
|
56
|
-
S3_BUCKET: z.string().min(1, 'S3 bucket is required'),
|
|
57
|
-
S3_REGION: z.string().default('garage'),
|
|
58
|
-
S3_ACCESS_KEY_ID: z.string().min(1, 'S3 access key is required'),
|
|
59
|
-
S3_SECRET_ACCESS_KEY: z.string().min(1, 'S3 secret access key is required'),
|
|
60
|
-
ATTACHMENT_URL_EXPIRES_IN: z.coerce.number().positive('Attachment URL expiry must be positive').default(1800),
|
|
61
|
-
} as const satisfies Record<string, ZodTypeAny>
|
|
62
|
-
|
|
63
|
-
export const loggingEnvShape = { LOG_LEVEL: z.enum(logLevelValues).default('info') } as const satisfies Record<
|
|
64
|
-
string,
|
|
65
|
-
ZodTypeAny
|
|
66
|
-
>
|
|
67
|
-
|
|
68
|
-
export const githubAppEnvShape = {
|
|
69
|
-
GITHUB_APP_ID: z.string().optional(),
|
|
70
|
-
GITHUB_PRIVATE_KEY: z.string().optional(),
|
|
71
|
-
GITHUB_APP_SLUG: z.string().optional(),
|
|
72
|
-
} as const satisfies Record<string, ZodTypeAny>
|
|
73
|
-
|
|
74
|
-
export const linearOauthEnvShape = {
|
|
75
|
-
LINEAR_CLIENT_ID: z.string().optional(),
|
|
76
|
-
LINEAR_CLIENT_SECRET: z.string().optional(),
|
|
77
|
-
LINEAR_REDIRECT_URI: z.string().optional(),
|
|
78
|
-
} as const satisfies Record<string, ZodTypeAny>
|
|
79
|
-
|
|
80
|
-
export const marketingStudioEnvShape = {
|
|
81
|
-
MARKETING_STUDIO_BASE_URL: z.string().url().optional(),
|
|
82
|
-
MARKETING_STUDIO_ADMIN_API_KEY: z.string().min(1).optional(),
|
|
83
|
-
MARKETING_STUDIO_WEBHOOK_SIGNING_SECRET: z.string().min(1).optional(),
|
|
84
|
-
} as const satisfies Record<string, ZodTypeAny>
|
|
85
|
-
|
|
86
|
-
export function envKeys<TShape extends Record<string, ZodTypeAny>>(shape: TShape) {
|
|
87
|
-
return Object.freeze(Object.keys(shape)) as readonly Extract<keyof TShape, string>[]
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const lotaSdkEnvSchema = z.object({
|
|
91
|
-
...aiGatewayEnvShape,
|
|
92
|
-
...memorySearchEnvShape,
|
|
93
|
-
...redisEnvShape,
|
|
94
|
-
...loggingEnvShape,
|
|
95
|
-
...s3StorageEnvShape,
|
|
96
|
-
...firecrawlEnvShape,
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
export const lotaSdkEnvKeys = Object.freeze([
|
|
100
|
-
...envKeys(aiGatewayEnvShape),
|
|
101
|
-
...envKeys(memorySearchEnvShape),
|
|
102
|
-
...envKeys(redisEnvShape),
|
|
103
|
-
...envKeys(loggingEnvShape),
|
|
104
|
-
...envKeys(s3StorageEnvShape),
|
|
105
|
-
...envKeys(firecrawlEnvShape),
|
|
106
|
-
]) as readonly string[]
|
|
107
|
-
|
|
108
|
-
type LotaSdkEnv = z.infer<typeof lotaSdkEnvSchema>
|
|
109
|
-
|
|
110
|
-
let _env: LotaSdkEnv | undefined
|
|
111
|
-
|
|
112
|
-
export function setEnv(value: LotaSdkEnv): void {
|
|
113
|
-
_env = value
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export const env: LotaSdkEnv = new Proxy({} as LotaSdkEnv, {
|
|
117
|
-
get(_target, prop: string) {
|
|
118
|
-
if (!_env) throw new Error(`lota-sdk env not configured. Call setEnv() before accessing env.${prop}`)
|
|
119
|
-
return (_env as Record<string, unknown>)[prop]
|
|
120
|
-
},
|
|
121
|
-
})
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type AgentSkill = string
|