@lota-sdk/core 0.1.5
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 +55 -0
- package/infrastructure/schema/01_memory.surql +47 -0
- package/infrastructure/schema/02_execution_plan.surql +62 -0
- package/infrastructure/schema/03_learned_skill.surql +32 -0
- package/infrastructure/schema/04_runtime_bootstrap.surql +8 -0
- package/package.json +128 -0
- package/src/ai/definitions.ts +308 -0
- package/src/bifrost/bifrost.ts +256 -0
- package/src/config/agent-defaults.ts +99 -0
- package/src/config/constants.ts +33 -0
- package/src/config/env-shapes.ts +122 -0
- package/src/config/logger.ts +29 -0
- package/src/config/model-constants.ts +31 -0
- package/src/config/search.ts +17 -0
- package/src/config/workstream-defaults.ts +68 -0
- package/src/db/base.service.ts +55 -0
- package/src/db/cursor-pagination.ts +73 -0
- package/src/db/memory-query-builder.ts +207 -0
- package/src/db/memory-store.helpers.ts +118 -0
- package/src/db/memory-store.rows.ts +29 -0
- package/src/db/memory-store.ts +974 -0
- package/src/db/memory-types.ts +193 -0
- package/src/db/memory.ts +505 -0
- package/src/db/record-id.ts +78 -0
- package/src/db/service.ts +932 -0
- package/src/db/startup.ts +152 -0
- package/src/db/tables.ts +20 -0
- package/src/document/org-document-chunking.ts +224 -0
- package/src/document/parsing.ts +40 -0
- package/src/embeddings/provider.ts +76 -0
- package/src/index.ts +302 -0
- package/src/queues/context-compaction.queue.ts +82 -0
- package/src/queues/document-processor.queue.ts +118 -0
- package/src/queues/memory-consolidation.queue.ts +65 -0
- package/src/queues/post-chat-memory.queue.ts +128 -0
- package/src/queues/recent-activity-title-refinement.queue.ts +69 -0
- package/src/queues/regular-chat-memory-digest.config.ts +12 -0
- package/src/queues/regular-chat-memory-digest.queue.ts +73 -0
- package/src/queues/skill-extraction.config.ts +9 -0
- package/src/queues/skill-extraction.queue.ts +62 -0
- package/src/redis/connection.ts +176 -0
- package/src/redis/index.ts +30 -0
- package/src/redis/org-memory-lock.ts +43 -0
- package/src/redis/redis-lease-lock.ts +158 -0
- package/src/runtime/agent-contract.ts +1 -0
- package/src/runtime/agent-prompt-context.ts +119 -0
- package/src/runtime/agent-runtime-policy.ts +192 -0
- package/src/runtime/agent-stream-helpers.ts +117 -0
- package/src/runtime/agent-types.ts +22 -0
- package/src/runtime/approval-continuation.ts +16 -0
- package/src/runtime/chat-attachments.ts +46 -0
- package/src/runtime/chat-message.ts +10 -0
- package/src/runtime/chat-request-routing.ts +21 -0
- package/src/runtime/chat-run-orchestration.ts +25 -0
- package/src/runtime/chat-run-registry.ts +20 -0
- package/src/runtime/chat-types.ts +18 -0
- package/src/runtime/context-compaction-constants.ts +11 -0
- package/src/runtime/context-compaction-runtime.ts +86 -0
- package/src/runtime/context-compaction.ts +909 -0
- package/src/runtime/execution-plan.ts +59 -0
- package/src/runtime/helper-model.ts +405 -0
- package/src/runtime/indexed-repositories-policy.ts +28 -0
- package/src/runtime/instruction-sections.ts +8 -0
- package/src/runtime/llm-content.ts +71 -0
- package/src/runtime/memory-block.ts +264 -0
- package/src/runtime/memory-digest-policy.ts +14 -0
- package/src/runtime/memory-format.ts +8 -0
- package/src/runtime/memory-pipeline.ts +570 -0
- package/src/runtime/memory-prompts-fact.ts +47 -0
- package/src/runtime/memory-prompts-parse.ts +3 -0
- package/src/runtime/memory-prompts-update.ts +37 -0
- package/src/runtime/memory-scope.ts +43 -0
- package/src/runtime/plugin-types.ts +10 -0
- package/src/runtime/retrieval-adapters.ts +25 -0
- package/src/runtime/retrieval-pipeline.ts +3 -0
- package/src/runtime/runtime-extensions.ts +154 -0
- package/src/runtime/skill-extraction-policy.ts +3 -0
- package/src/runtime/team-consultation-orchestrator.ts +245 -0
- package/src/runtime/team-consultation-prompts.ts +32 -0
- package/src/runtime/title-helpers.ts +12 -0
- package/src/runtime/turn-lifecycle.ts +28 -0
- package/src/runtime/workstream-chat-helpers.ts +187 -0
- package/src/runtime/workstream-routing-policy.ts +301 -0
- package/src/runtime/workstream-state.ts +261 -0
- package/src/services/attachment.service.ts +159 -0
- package/src/services/chat-attachments.service.ts +17 -0
- package/src/services/chat-run-registry.service.ts +3 -0
- package/src/services/context-compaction-runtime.ts +13 -0
- package/src/services/context-compaction.service.ts +115 -0
- package/src/services/document-chunk.service.ts +141 -0
- package/src/services/execution-plan.service.ts +890 -0
- package/src/services/learned-skill.service.ts +328 -0
- package/src/services/memory-assessment.service.ts +43 -0
- package/src/services/memory.service.ts +807 -0
- package/src/services/memory.utils.ts +84 -0
- package/src/services/mutating-approval.service.ts +110 -0
- package/src/services/recent-activity-title.service.ts +74 -0
- package/src/services/recent-activity.service.ts +397 -0
- package/src/services/workstream-change-tracker.service.ts +313 -0
- package/src/services/workstream-message.service.ts +283 -0
- package/src/services/workstream-title.service.ts +58 -0
- package/src/services/workstream-turn-preparation.ts +1340 -0
- package/src/services/workstream-turn.ts +37 -0
- package/src/services/workstream.service.ts +854 -0
- package/src/services/workstream.types.ts +118 -0
- package/src/storage/attachment-parser.ts +101 -0
- package/src/storage/attachment-storage.service.ts +391 -0
- package/src/storage/attachments.types.ts +11 -0
- package/src/storage/attachments.utils.ts +58 -0
- package/src/storage/generated-document-storage.service.ts +55 -0
- package/src/system-agents/agent-result.ts +27 -0
- package/src/system-agents/context-compacter.agent.ts +46 -0
- package/src/system-agents/delegated-agent-factory.ts +177 -0
- package/src/system-agents/helper-agent-options.ts +20 -0
- package/src/system-agents/memory-reranker.agent.ts +38 -0
- package/src/system-agents/memory.agent.ts +58 -0
- package/src/system-agents/recent-activity-title-refiner.agent.ts +53 -0
- package/src/system-agents/regular-chat-memory-digest.agent.ts +75 -0
- package/src/system-agents/researcher.agent.ts +34 -0
- package/src/system-agents/skill-extractor.agent.ts +88 -0
- package/src/system-agents/skill-manager.agent.ts +80 -0
- package/src/system-agents/title-generator.agent.ts +42 -0
- package/src/system-agents/workstream-tracker.agent.ts +58 -0
- package/src/tools/execution-plan.tool.ts +163 -0
- package/src/tools/fetch-webpage.tool.ts +132 -0
- package/src/tools/firecrawl-client.ts +12 -0
- package/src/tools/memory-block.tool.ts +55 -0
- package/src/tools/read-file-parts.tool.ts +80 -0
- package/src/tools/remember-memory.tool.ts +85 -0
- package/src/tools/research-topic.tool.ts +15 -0
- package/src/tools/search-tools.ts +55 -0
- package/src/tools/search-web.tool.ts +175 -0
- package/src/tools/team-think.tool.ts +125 -0
- package/src/tools/tool-contract.ts +21 -0
- package/src/tools/user-questions.tool.ts +18 -0
- package/src/utils/async.ts +50 -0
- package/src/utils/date-time.ts +34 -0
- package/src/utils/error.ts +10 -0
- package/src/utils/errors.ts +28 -0
- package/src/utils/hono-error-handler.ts +71 -0
- package/src/utils/string.ts +51 -0
- package/src/workers/bootstrap.ts +44 -0
- package/src/workers/memory-consolidation.worker.ts +318 -0
- package/src/workers/regular-chat-memory-digest.helpers.ts +100 -0
- package/src/workers/regular-chat-memory-digest.runner.ts +363 -0
- package/src/workers/regular-chat-memory-digest.worker.ts +22 -0
- package/src/workers/skill-extraction.runner.ts +331 -0
- package/src/workers/skill-extraction.worker.ts +22 -0
- package/src/workers/utils/repo-indexer-chunker.ts +331 -0
- package/src/workers/utils/repo-structure-extractor.ts +645 -0
- package/src/workers/utils/repomix-process-concurrency.ts +65 -0
- package/src/workers/utils/sandbox-error.ts +5 -0
- package/src/workers/worker-utils.ts +182 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { recordIdStringSchema } from '@lota-sdk/shared/schemas/common'
|
|
2
|
+
import { BoundQuery } from 'surrealdb'
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
|
|
5
|
+
import { renderLearnedSkillInstructions } from '../ai/definitions'
|
|
6
|
+
import { serverLogger } from '../config/logger'
|
|
7
|
+
import { ensureRecordId } from '../db/record-id'
|
|
8
|
+
import { databaseService } from '../db/service'
|
|
9
|
+
import { TABLES } from '../db/tables'
|
|
10
|
+
import { createDefaultEmbeddings } from '../embeddings/provider'
|
|
11
|
+
|
|
12
|
+
const embeddings = createDefaultEmbeddings()
|
|
13
|
+
|
|
14
|
+
const PROMOTION_MIN_USES = 5
|
|
15
|
+
const PROMOTION_MIN_SUCCESS_RATE = 0.6
|
|
16
|
+
|
|
17
|
+
const LearnedSkillRowSchema = z.object({
|
|
18
|
+
id: recordIdStringSchema,
|
|
19
|
+
name: z.string(),
|
|
20
|
+
description: z.string(),
|
|
21
|
+
instructions: z.string(),
|
|
22
|
+
triggers: z.array(z.string()).default([]),
|
|
23
|
+
tags: z.array(z.string()).default([]),
|
|
24
|
+
examples: z.array(z.string()).default([]),
|
|
25
|
+
version: z.number().int().default(1),
|
|
26
|
+
sourceType: z.string(),
|
|
27
|
+
organizationId: recordIdStringSchema,
|
|
28
|
+
agentId: z.string().optional(),
|
|
29
|
+
status: z.enum(['learned', 'verified', 'promoted', 'archived']).default('learned'),
|
|
30
|
+
confidence: z.number().min(0).max(1).default(0.5),
|
|
31
|
+
usageCount: z.number().int().default(0),
|
|
32
|
+
successCount: z.number().int().default(0),
|
|
33
|
+
supersedes: recordIdStringSchema.optional(),
|
|
34
|
+
hash: z.string(),
|
|
35
|
+
createdAt: z.coerce.date(),
|
|
36
|
+
updatedAt: z.coerce.date().optional(),
|
|
37
|
+
lastUsedAt: z.coerce.date().optional(),
|
|
38
|
+
archivedAt: z.coerce.date().optional(),
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
type LearnedSkillRow = z.infer<typeof LearnedSkillRowSchema>
|
|
42
|
+
|
|
43
|
+
const SearchResultRowSchema = z.object({
|
|
44
|
+
id: z.string(),
|
|
45
|
+
name: z.string(),
|
|
46
|
+
description: z.string(),
|
|
47
|
+
instructions: z.string(),
|
|
48
|
+
confidence: z.number(),
|
|
49
|
+
similarity: z.number(),
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
type SearchResultRow = z.infer<typeof SearchResultRowSchema>
|
|
53
|
+
|
|
54
|
+
export interface CreateLearnedSkillInput {
|
|
55
|
+
name: string
|
|
56
|
+
description: string
|
|
57
|
+
instructions: string
|
|
58
|
+
triggers: string[]
|
|
59
|
+
tags: string[]
|
|
60
|
+
examples: string[]
|
|
61
|
+
sourceType: string
|
|
62
|
+
organizationId: string
|
|
63
|
+
agentId: string | null
|
|
64
|
+
confidence: number
|
|
65
|
+
embedding: number[]
|
|
66
|
+
hash: string
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface UpdateLearnedSkillInput {
|
|
70
|
+
name?: string
|
|
71
|
+
description?: string
|
|
72
|
+
instructions?: string
|
|
73
|
+
triggers?: string[]
|
|
74
|
+
tags?: string[]
|
|
75
|
+
examples?: string[]
|
|
76
|
+
confidence?: number
|
|
77
|
+
version?: number
|
|
78
|
+
embedding?: number[]
|
|
79
|
+
hash?: string
|
|
80
|
+
supersedes?: string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface RetrieveForTurnParams {
|
|
84
|
+
orgId: string
|
|
85
|
+
agentId: string
|
|
86
|
+
query: string
|
|
87
|
+
limit: number
|
|
88
|
+
minConfidence: number
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
class LearnedSkillService {
|
|
92
|
+
async create(input: CreateLearnedSkillInput): Promise<LearnedSkillRow> {
|
|
93
|
+
const orgRef = ensureRecordId(input.organizationId, TABLES.ORGANIZATION)
|
|
94
|
+
|
|
95
|
+
const data: Record<string, unknown> = {
|
|
96
|
+
name: input.name,
|
|
97
|
+
description: input.description,
|
|
98
|
+
instructions: input.instructions,
|
|
99
|
+
triggers: input.triggers,
|
|
100
|
+
tags: input.tags,
|
|
101
|
+
examples: input.examples,
|
|
102
|
+
sourceType: input.sourceType,
|
|
103
|
+
organizationId: orgRef,
|
|
104
|
+
agentId: input.agentId,
|
|
105
|
+
confidence: input.confidence,
|
|
106
|
+
embedding: input.embedding,
|
|
107
|
+
hash: input.hash,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return databaseService.create(TABLES.LEARNED_SKILL, data, LearnedSkillRowSchema)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async update(skillId: string, input: UpdateLearnedSkillInput): Promise<LearnedSkillRow> {
|
|
114
|
+
const ref = ensureRecordId(skillId, TABLES.LEARNED_SKILL)
|
|
115
|
+
const data: Record<string, unknown> = { updatedAt: new Date().toISOString() }
|
|
116
|
+
|
|
117
|
+
if (input.name !== undefined) data.name = input.name
|
|
118
|
+
if (input.description !== undefined) data.description = input.description
|
|
119
|
+
if (input.instructions !== undefined) data.instructions = input.instructions
|
|
120
|
+
if (input.triggers !== undefined) data.triggers = input.triggers
|
|
121
|
+
if (input.tags !== undefined) data.tags = input.tags
|
|
122
|
+
if (input.examples !== undefined) data.examples = input.examples
|
|
123
|
+
if (input.confidence !== undefined) data.confidence = input.confidence
|
|
124
|
+
if (input.version !== undefined) data.version = input.version
|
|
125
|
+
if (input.embedding !== undefined) data.embedding = input.embedding
|
|
126
|
+
if (input.hash !== undefined) data.hash = input.hash
|
|
127
|
+
if (input.supersedes !== undefined) data.supersedes = ensureRecordId(input.supersedes, TABLES.LEARNED_SKILL)
|
|
128
|
+
|
|
129
|
+
const updated = await databaseService.update(TABLES.LEARNED_SKILL, ref, data, LearnedSkillRowSchema)
|
|
130
|
+
if (!updated) throw new Error(`Learned skill ${skillId} not found`)
|
|
131
|
+
return updated
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async archive(skillId: string): Promise<void> {
|
|
135
|
+
const ref = ensureRecordId(skillId, TABLES.LEARNED_SKILL)
|
|
136
|
+
await databaseService.update(
|
|
137
|
+
TABLES.LEARNED_SKILL,
|
|
138
|
+
ref,
|
|
139
|
+
{ status: 'archived', archivedAt: new Date().toISOString(), updatedAt: new Date().toISOString() },
|
|
140
|
+
LearnedSkillRowSchema,
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async getById(skillId: string): Promise<LearnedSkillRow | null> {
|
|
145
|
+
return databaseService.findOne(
|
|
146
|
+
TABLES.LEARNED_SKILL,
|
|
147
|
+
{ id: ensureRecordId(skillId, TABLES.LEARNED_SKILL) },
|
|
148
|
+
LearnedSkillRowSchema,
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async searchForTurn(params: RetrieveForTurnParams): Promise<SearchResultRow[]> {
|
|
153
|
+
const orgRef = ensureRecordId(params.orgId, TABLES.ORGANIZATION)
|
|
154
|
+
const queryEmbedding = await embeddings.embedQuery(params.query)
|
|
155
|
+
if (queryEmbedding.length === 0) return []
|
|
156
|
+
|
|
157
|
+
const candidateLimit = params.limit * 4
|
|
158
|
+
const sql = `
|
|
159
|
+
LET $candidateRows = (
|
|
160
|
+
SELECT
|
|
161
|
+
id,
|
|
162
|
+
vector::distance::knn() AS distance
|
|
163
|
+
FROM ${TABLES.LEARNED_SKILL}
|
|
164
|
+
WHERE organizationId = $organizationId
|
|
165
|
+
AND status IN ['learned', 'verified', 'promoted']
|
|
166
|
+
AND archivedAt IS NONE
|
|
167
|
+
AND confidence >= $minConfidence
|
|
168
|
+
AND (agentId IS NONE OR agentId = $agentId)
|
|
169
|
+
AND embedding <|${candidateLimit}|> $embedding
|
|
170
|
+
ORDER BY distance ASC
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
SELECT
|
|
174
|
+
type::string(id) AS id,
|
|
175
|
+
name,
|
|
176
|
+
description,
|
|
177
|
+
instructions,
|
|
178
|
+
confidence,
|
|
179
|
+
vector::similarity::cosine(embedding, $embedding) AS similarity
|
|
180
|
+
FROM ${TABLES.LEARNED_SKILL}
|
|
181
|
+
WHERE id IN $candidateRows.id
|
|
182
|
+
ORDER BY similarity DESC
|
|
183
|
+
LIMIT $limit
|
|
184
|
+
`
|
|
185
|
+
|
|
186
|
+
const rows = await databaseService.query<unknown>(
|
|
187
|
+
new BoundQuery(sql, {
|
|
188
|
+
organizationId: orgRef,
|
|
189
|
+
embedding: queryEmbedding,
|
|
190
|
+
agentId: params.agentId,
|
|
191
|
+
minConfidence: params.minConfidence,
|
|
192
|
+
limit: params.limit,
|
|
193
|
+
}),
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
return rows.map((row) => SearchResultRowSchema.parse(row)).filter((row) => row.similarity >= 0.3)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async retrieveForTurn(params: RetrieveForTurnParams): Promise<string | undefined> {
|
|
200
|
+
const results = await this.searchForTurn(params)
|
|
201
|
+
if (results.length === 0) return undefined
|
|
202
|
+
|
|
203
|
+
for (const result of results) {
|
|
204
|
+
void this.recordUsage(result.id).catch((error) => {
|
|
205
|
+
serverLogger.warn`Failed to record learned skill usage for ${result.id}: ${error}`
|
|
206
|
+
})
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const section = renderLearnedSkillInstructions(
|
|
210
|
+
results.map((row) => ({ name: row.name, instructions: row.instructions })),
|
|
211
|
+
)
|
|
212
|
+
return section || undefined
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async recordUsage(skillId: string): Promise<void> {
|
|
216
|
+
const ref = ensureRecordId(skillId, TABLES.LEARNED_SKILL)
|
|
217
|
+
await databaseService.query<unknown>(
|
|
218
|
+
new BoundQuery(`UPDATE ${TABLES.LEARNED_SKILL} SET usageCount += 1, lastUsedAt = time::now() WHERE id = $id`, {
|
|
219
|
+
id: ref,
|
|
220
|
+
}),
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async recordSuccess(skillId: string): Promise<void> {
|
|
225
|
+
const ref = ensureRecordId(skillId, TABLES.LEARNED_SKILL)
|
|
226
|
+
await databaseService.query<unknown>(
|
|
227
|
+
new BoundQuery(`UPDATE ${TABLES.LEARNED_SKILL} SET successCount += 1 WHERE id = $id`, { id: ref }),
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async promoteIfEligible(skillId: string): Promise<boolean> {
|
|
232
|
+
const skill = await this.getById(skillId)
|
|
233
|
+
if (!skill) return false
|
|
234
|
+
if (skill.status !== 'learned') return false
|
|
235
|
+
if (skill.usageCount < PROMOTION_MIN_USES) return false
|
|
236
|
+
|
|
237
|
+
const successRate = skill.successCount / skill.usageCount
|
|
238
|
+
if (successRate < PROMOTION_MIN_SUCCESS_RATE) return false
|
|
239
|
+
|
|
240
|
+
const ref = ensureRecordId(skillId, TABLES.LEARNED_SKILL)
|
|
241
|
+
await databaseService.update(
|
|
242
|
+
TABLES.LEARNED_SKILL,
|
|
243
|
+
ref,
|
|
244
|
+
{ status: 'verified', confidence: Math.min(skill.confidence + 0.1, 1.0), updatedAt: new Date().toISOString() },
|
|
245
|
+
LearnedSkillRowSchema,
|
|
246
|
+
)
|
|
247
|
+
return true
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async findMostSimilar(orgId: string, description: string): Promise<LearnedSkillRow | null> {
|
|
251
|
+
const orgRef = ensureRecordId(orgId, TABLES.ORGANIZATION)
|
|
252
|
+
const descEmbedding = await embeddings.embedQuery(description)
|
|
253
|
+
if (descEmbedding.length === 0) return null
|
|
254
|
+
|
|
255
|
+
const sql = `
|
|
256
|
+
LET $candidateRows = (
|
|
257
|
+
SELECT
|
|
258
|
+
id,
|
|
259
|
+
vector::distance::knn() AS distance
|
|
260
|
+
FROM ${TABLES.LEARNED_SKILL}
|
|
261
|
+
WHERE organizationId = $organizationId
|
|
262
|
+
AND status IN ['learned', 'verified', 'promoted']
|
|
263
|
+
AND archivedAt IS NONE
|
|
264
|
+
AND embedding <|3|> $embedding
|
|
265
|
+
ORDER BY distance ASC
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
SELECT *,
|
|
269
|
+
type::string(id) AS id,
|
|
270
|
+
type::string(organizationId) AS organizationId,
|
|
271
|
+
vector::similarity::cosine(embedding, $embedding) AS similarity
|
|
272
|
+
FROM ${TABLES.LEARNED_SKILL}
|
|
273
|
+
WHERE id IN $candidateRows.id
|
|
274
|
+
ORDER BY similarity DESC
|
|
275
|
+
LIMIT 1
|
|
276
|
+
`
|
|
277
|
+
|
|
278
|
+
const rows = await databaseService.query<unknown>(
|
|
279
|
+
new BoundQuery(sql, { organizationId: orgRef, embedding: descEmbedding }),
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
if (rows.length === 0) return null
|
|
283
|
+
return LearnedSkillRowSchema.parse(rows[0])
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async listForOrg(orgId: string): Promise<LearnedSkillRow[]> {
|
|
287
|
+
const orgRef = ensureRecordId(orgId, TABLES.ORGANIZATION)
|
|
288
|
+
return databaseService.queryMany(
|
|
289
|
+
new BoundQuery(
|
|
290
|
+
`SELECT *, type::string(id) AS id, type::string(organizationId) AS organizationId
|
|
291
|
+
FROM ${TABLES.LEARNED_SKILL}
|
|
292
|
+
WHERE organizationId = $organizationId
|
|
293
|
+
AND status IN ['learned', 'verified', 'promoted']
|
|
294
|
+
AND archivedAt IS NONE
|
|
295
|
+
ORDER BY createdAt DESC`,
|
|
296
|
+
{ organizationId: orgRef },
|
|
297
|
+
),
|
|
298
|
+
LearnedSkillRowSchema,
|
|
299
|
+
)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
generateHash(description: string, instructions: string): string {
|
|
303
|
+
const hasher = new Bun.CryptoHasher('sha256')
|
|
304
|
+
hasher.update(description.trim().toLowerCase())
|
|
305
|
+
hasher.update(instructions.trim().toLowerCase())
|
|
306
|
+
return hasher.digest('hex')
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async findByHash(orgId: string, hash: string): Promise<LearnedSkillRow | null> {
|
|
310
|
+
const orgRef = ensureRecordId(orgId, TABLES.ORGANIZATION)
|
|
311
|
+
const rows = await databaseService.queryMany(
|
|
312
|
+
new BoundQuery(
|
|
313
|
+
`SELECT *, type::string(id) AS id, type::string(organizationId) AS organizationId
|
|
314
|
+
FROM ${TABLES.LEARNED_SKILL}
|
|
315
|
+
WHERE organizationId = $organizationId
|
|
316
|
+
AND hash = $hash
|
|
317
|
+
AND status IN ['learned', 'verified', 'promoted']
|
|
318
|
+
AND archivedAt IS NONE
|
|
319
|
+
LIMIT 1`,
|
|
320
|
+
{ organizationId: orgRef, hash },
|
|
321
|
+
),
|
|
322
|
+
LearnedSkillRowSchema,
|
|
323
|
+
)
|
|
324
|
+
return rows[0] ?? null
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export const learnedSkillService = new LearnedSkillService()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import { MemoryImportanceAssessmentSchema } from '../db/memory-types'
|
|
4
|
+
import { createHelperModelRuntime } from '../runtime/helper-model'
|
|
5
|
+
import { createOrgMemoryAgent } from '../system-agents/memory.agent'
|
|
6
|
+
|
|
7
|
+
type MemoryImportanceAssessment = z.infer<typeof MemoryImportanceAssessmentSchema>
|
|
8
|
+
const MEMORY_IMPORTANCE_ASSESSMENT_TIMEOUT_MS = 10 * 60 * 1000
|
|
9
|
+
const helperModelRuntime = createHelperModelRuntime()
|
|
10
|
+
|
|
11
|
+
export function clampMemoryImportance(value: number): number {
|
|
12
|
+
return Math.max(0.2, Math.min(0.95, value))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function assessMemoryImportance(params: {
|
|
16
|
+
content: string
|
|
17
|
+
targetScope: 'agent' | 'global'
|
|
18
|
+
tag?: string
|
|
19
|
+
}): Promise<MemoryImportanceAssessment> {
|
|
20
|
+
const prompt = [
|
|
21
|
+
'Assess whether this memory should be persisted in long-term memory.',
|
|
22
|
+
`targetScope: ${params.targetScope}`,
|
|
23
|
+
`memory: ${params.content}`,
|
|
24
|
+
'',
|
|
25
|
+
'Rules:',
|
|
26
|
+
'- importance: number between 0 and 1 for long-term usefulness.',
|
|
27
|
+
'- durability: core|standard|ephemeral.',
|
|
28
|
+
'- classification: durable|transient|uncertain.',
|
|
29
|
+
'- rationale: short and concrete.',
|
|
30
|
+
'',
|
|
31
|
+
'The caller enforces a structured output schema.',
|
|
32
|
+
'Return only schema fields.',
|
|
33
|
+
].join('\n')
|
|
34
|
+
|
|
35
|
+
return await helperModelRuntime.generateHelperStructured({
|
|
36
|
+
tag: params.tag ?? 'memory-importance-assessment',
|
|
37
|
+
createAgent: createOrgMemoryAgent,
|
|
38
|
+
systemPrompt: 'You are a strict long-term memory quality assessor for an AI agent.',
|
|
39
|
+
messages: [{ role: 'user', content: prompt }],
|
|
40
|
+
schema: MemoryImportanceAssessmentSchema,
|
|
41
|
+
timeoutMs: MEMORY_IMPORTANCE_ASSESSMENT_TIMEOUT_MS,
|
|
42
|
+
})
|
|
43
|
+
}
|