@lota-sdk/core 0.1.13 → 0.1.15
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 +5 -5
- package/src/ai/embedding-cache.ts +7 -6
- package/src/ai/index.ts +1 -0
- package/src/bifrost/bifrost.ts +12 -7
- package/src/config/agent-defaults.ts +1 -1
- package/src/config/logger.ts +7 -9
- package/src/{runtime.ts → create-runtime.ts} +6 -6
- package/src/db/cursor-pagination.ts +1 -1
- package/src/db/memory-store.ts +10 -6
- package/src/db/memory.ts +6 -4
- package/src/db/schema-fingerprint.ts +1 -0
- package/src/db/service.ts +45 -51
- package/src/db/startup.ts +3 -3
- package/src/index.ts +1 -1
- package/src/queues/context-compaction.queue.ts +4 -8
- package/src/queues/document-processor.queue.ts +7 -7
- package/src/queues/memory-consolidation.queue.ts +7 -8
- package/src/queues/post-chat-memory.queue.ts +2 -6
- package/src/queues/recent-activity-title-refinement.queue.ts +2 -6
- package/src/queues/regular-chat-memory-digest.queue.ts +4 -7
- package/src/queues/skill-extraction.queue.ts +4 -7
- package/src/queues/workstream-title-generation.queue.ts +2 -6
- package/src/redis/connection.ts +6 -3
- package/src/redis/index.ts +1 -0
- package/src/redis/org-memory-lock.ts +1 -1
- package/src/redis/redis-lease-lock.ts +41 -8
- package/src/runtime/agent-stream-helpers.ts +2 -1
- package/src/runtime/context-compaction-constants.ts +1 -1
- package/src/runtime/context-compaction-runtime.ts +6 -4
- package/src/runtime/context-compaction.ts +19 -38
- package/src/runtime/execution-plan.ts +2 -2
- package/src/runtime/helper-model.ts +3 -1
- package/src/runtime/index.ts +12 -1
- package/src/runtime/memory-block.ts +3 -2
- package/src/runtime/memory-pipeline.ts +24 -5
- package/src/runtime/plugin-types.ts +1 -1
- package/src/runtime/runtime-extensions.ts +89 -13
- package/src/runtime/title-helpers.ts +11 -2
- package/src/runtime/workstream-chat-helpers.ts +5 -6
- package/src/runtime/workstream-routing-policy.ts +0 -30
- package/src/runtime/workstream-state.ts +17 -7
- package/src/services/attachment.service.ts +1 -1
- package/src/services/context-compaction.service.ts +3 -3
- package/src/services/document-chunk.service.ts +37 -32
- package/src/services/execution-plan.service.ts +2 -0
- package/src/services/learned-skill.service.ts +6 -10
- package/src/services/{memory.utils.ts → memory-utils.ts} +4 -8
- package/src/services/memory.service.ts +21 -18
- package/src/services/organization-member.service.ts +1 -1
- package/src/services/plan-artifact.service.ts +1 -0
- package/src/services/plan-executor.service.ts +2 -18
- package/src/services/plan-helpers.ts +15 -0
- package/src/services/plan-validator.service.ts +3 -18
- package/src/services/recent-activity-title.service.ts +3 -10
- package/src/services/recent-activity.service.ts +6 -12
- package/src/services/workstream-message.service.ts +26 -16
- package/src/services/workstream-title.service.ts +1 -9
- package/src/services/{workstream-turn-preparation.ts → workstream-turn-preparation.service.ts} +401 -314
- package/src/services/workstream-turn.ts +2 -2
- package/src/services/workstream.service.ts +22 -10
- package/src/services/workstream.types.ts +7 -16
- package/src/storage/attachment-storage.service.ts +4 -4
- package/src/storage/{attachments.utils.ts → attachment-utils.ts} +1 -4
- package/src/storage/index.ts +2 -2
- package/src/system-agents/{context-compacter.agent.ts → context-compaction.agent.ts} +4 -4
- package/src/system-agents/delegated-agent-factory.ts +3 -2
- package/src/system-agents/index.ts +8 -0
- package/src/system-agents/memory-reranker.agent.ts +1 -1
- package/src/system-agents/memory.agent.ts +1 -1
- package/src/system-agents/recent-activity-title-refiner.agent.ts +1 -1
- package/src/tools/execution-plan.tool.ts +6 -2
- package/src/tools/fetch-webpage.tool.ts +20 -18
- package/src/tools/index.ts +2 -2
- package/src/tools/read-file-parts.tool.ts +1 -1
- package/src/tools/search-web.tool.ts +18 -15
- package/src/tools/{search-tools.ts → search.tool.ts} +1 -1
- package/src/tools/team-think.tool.ts +9 -5
- package/src/tools/{tool-contract.ts → tool-contracts.ts} +9 -2
- package/src/utils/async.ts +1 -1
- package/src/utils/errors.ts +15 -0
- package/src/utils/hono-error-handler.ts +1 -2
- package/src/utils/index.ts +10 -2
- package/src/utils/string.ts +14 -0
- package/src/workers/bootstrap.ts +2 -2
- package/src/workers/memory-consolidation.worker.ts +12 -12
- package/src/workers/regular-chat-memory-digest.helpers.ts +2 -7
- package/src/workers/regular-chat-memory-digest.runner.ts +9 -103
- package/src/workers/skill-extraction.runner.ts +7 -101
- package/src/workers/utils/file-section-chunker.ts +5 -3
- package/src/workers/utils/workstream-message-query.ts +106 -0
- package/src/workers/worker-utils.ts +4 -0
- package/src/runtime/retrieval-pipeline.ts +0 -3
- package/src/utils/error.ts +0 -10
- /package/src/services/{context-compaction-runtime.ts → context-compaction-runtime.singleton.ts} +0 -0
- /package/src/storage/{attachments.types.ts → attachment-types.ts} +0 -0
package/src/db/service.ts
CHANGED
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
import type { ExprLike, Mutation, SurrealTransaction, Values } from 'surrealdb'
|
|
13
13
|
import type { z } from 'zod'
|
|
14
14
|
|
|
15
|
+
import { withTimeout } from '../utils/async'
|
|
16
|
+
import { isRecord } from '../utils/string'
|
|
15
17
|
import type { RecordIdInput } from './record-id'
|
|
16
18
|
import { ensureRecordId, readCustomStringValue } from './record-id'
|
|
17
19
|
import type { DatabaseTable } from './tables'
|
|
@@ -53,11 +55,12 @@ interface FindManyOptions {
|
|
|
53
55
|
type MutationBuilder = {
|
|
54
56
|
content: (data: Record<string, unknown>) => MutationBuilder
|
|
55
57
|
replace: (data: Record<string, unknown>) => MutationBuilder
|
|
56
|
-
patch: (data: Record<string, unknown>) => MutationBuilder
|
|
57
58
|
merge: (data: Record<string, unknown>) => MutationBuilder
|
|
58
59
|
output: (mode: 'after' | 'before') => Promise<unknown>
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
type RecordMutation = Extract<Mutation, 'content' | 'replace' | 'merge'>
|
|
63
|
+
|
|
61
64
|
export type CreateMutationBuilder = {
|
|
62
65
|
content: (data: Record<string, unknown>) => CreateMutationBuilder
|
|
63
66
|
output: (mode: 'after' | 'before') => Promise<unknown>
|
|
@@ -75,7 +78,7 @@ export interface DatabaseTransaction {
|
|
|
75
78
|
|
|
76
79
|
function configureMutation(
|
|
77
80
|
builder: MutationBuilder,
|
|
78
|
-
mutation:
|
|
81
|
+
mutation: RecordMutation,
|
|
79
82
|
data: Record<string, unknown>,
|
|
80
83
|
): MutationBuilder {
|
|
81
84
|
if (mutation === 'content') {
|
|
@@ -84,18 +87,11 @@ function configureMutation(
|
|
|
84
87
|
if (mutation === 'replace') {
|
|
85
88
|
return builder.replace(data)
|
|
86
89
|
}
|
|
87
|
-
if (mutation === 'patch') {
|
|
88
|
-
return builder.patch(data)
|
|
89
|
-
}
|
|
90
90
|
return builder.merge(data)
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
function isRecordValue(value: unknown): value is Record<string, unknown> {
|
|
94
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
93
|
function isBoundQueryLike(value: unknown): value is BoundQueryLike {
|
|
98
|
-
if (!
|
|
94
|
+
if (!isRecord(value)) {
|
|
99
95
|
return false
|
|
100
96
|
}
|
|
101
97
|
|
|
@@ -103,7 +99,7 @@ function isBoundQueryLike(value: unknown): value is BoundQueryLike {
|
|
|
103
99
|
return false
|
|
104
100
|
}
|
|
105
101
|
|
|
106
|
-
return value.bindings === undefined ||
|
|
102
|
+
return value.bindings === undefined || isRecord(value.bindings)
|
|
107
103
|
}
|
|
108
104
|
|
|
109
105
|
function toStringLikeValue(value: unknown): string | null {
|
|
@@ -124,23 +120,6 @@ const CONNECT_RETRY_BASE_DELAY_MS = 100
|
|
|
124
120
|
const CONNECT_RETRY_JITTER_MS = 50
|
|
125
121
|
const CONNECT_ATTEMPT_TIMEOUT_MS = 5_000
|
|
126
122
|
|
|
127
|
-
async function withTimeout<T>(promise: Promise<T>, timeoutMs: number, message: string): Promise<T> {
|
|
128
|
-
let timeoutId: ReturnType<typeof setTimeout> | undefined
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
return await Promise.race([
|
|
132
|
-
promise,
|
|
133
|
-
new Promise<T>((_, reject) => {
|
|
134
|
-
timeoutId = setTimeout(() => reject(new Error(message)), timeoutMs)
|
|
135
|
-
}),
|
|
136
|
-
])
|
|
137
|
-
} finally {
|
|
138
|
-
if (timeoutId) {
|
|
139
|
-
clearTimeout(timeoutId)
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
123
|
export class SurrealDBService {
|
|
145
124
|
private client: Surreal | null = null
|
|
146
125
|
private isConnected = false
|
|
@@ -254,7 +233,7 @@ export class SurrealDBService {
|
|
|
254
233
|
: { username: this.config.username ?? '', password: this.config.password ?? '' },
|
|
255
234
|
}),
|
|
256
235
|
CONNECT_ATTEMPT_TIMEOUT_MS,
|
|
257
|
-
`
|
|
236
|
+
`SurrealDB connect (${this.config.url})`,
|
|
258
237
|
)
|
|
259
238
|
|
|
260
239
|
this.isConnected = true
|
|
@@ -280,7 +259,7 @@ export class SurrealDBService {
|
|
|
280
259
|
}
|
|
281
260
|
}
|
|
282
261
|
|
|
283
|
-
this.toSurrealError(lastError)
|
|
262
|
+
return this.toSurrealError(lastError)
|
|
284
263
|
})()
|
|
285
264
|
|
|
286
265
|
try {
|
|
@@ -329,8 +308,16 @@ export class SurrealDBService {
|
|
|
329
308
|
|
|
330
309
|
private normalizeRecordId(id: unknown, table: DatabaseTable): ReturnType<typeof ensureRecordId> {
|
|
331
310
|
try {
|
|
332
|
-
|
|
311
|
+
const recordId = ensureRecordId(id as RecordIdInput, table)
|
|
312
|
+
const resolvedTable = String(recordId.table)
|
|
313
|
+
if (resolvedTable !== table) {
|
|
314
|
+
throw new SurrealDBError(`Record id table mismatch: expected "${table}" but got "${resolvedTable}"`)
|
|
315
|
+
}
|
|
316
|
+
return recordId
|
|
333
317
|
} catch (error) {
|
|
318
|
+
if (error instanceof SurrealDBError) {
|
|
319
|
+
throw error
|
|
320
|
+
}
|
|
334
321
|
if (error instanceof Error) {
|
|
335
322
|
throw new SurrealDBError(`Invalid record id for table "${table}": ${error.message}`, undefined, {
|
|
336
323
|
cause: error,
|
|
@@ -394,7 +381,7 @@ export class SurrealDBService {
|
|
|
394
381
|
return value.map((entry) => this.normalizeRuntimeValue(entry))
|
|
395
382
|
}
|
|
396
383
|
|
|
397
|
-
if (!
|
|
384
|
+
if (!isRecord(value)) {
|
|
398
385
|
return value
|
|
399
386
|
}
|
|
400
387
|
|
|
@@ -415,6 +402,8 @@ export class SurrealDBService {
|
|
|
415
402
|
return Object.fromEntries(entries.map(([key, entryValue]) => [key, this.normalizeRuntimeValue(entryValue)]))
|
|
416
403
|
}
|
|
417
404
|
|
|
405
|
+
// Cast is safe: normalizeRuntimeValue preserves Record shape when input is a Record
|
|
406
|
+
// (non-array objects are mapped entry-by-entry and returned as Object.fromEntries)
|
|
418
407
|
private normalizeBindings(bindings?: Record<string, unknown>): Record<string, unknown> | undefined {
|
|
419
408
|
if (!bindings) {
|
|
420
409
|
return undefined
|
|
@@ -423,6 +412,7 @@ export class SurrealDBService {
|
|
|
423
412
|
return this.normalizeRuntimeValue(bindings) as Record<string, unknown>
|
|
424
413
|
}
|
|
425
414
|
|
|
415
|
+
// Cast is safe: normalizeRuntimeValue preserves Record shape when input is a Record
|
|
426
416
|
private normalizeMutationData(data: Record<string, unknown>): Record<string, unknown> {
|
|
427
417
|
const normalized = this.normalizeRuntimeValue(data) as Record<string, unknown>
|
|
428
418
|
return this.stripNullValues(normalized)
|
|
@@ -496,7 +486,6 @@ export class SurrealDBService {
|
|
|
496
486
|
return {
|
|
497
487
|
content: (data) => this.wrapMutationBuilder(builder.content(this.normalizeMutationData(data))),
|
|
498
488
|
replace: (data) => this.wrapMutationBuilder(builder.replace(this.normalizeMutationData(data))),
|
|
499
|
-
patch: (data) => this.wrapMutationBuilder(builder.patch(this.normalizeMutationData(data))),
|
|
500
489
|
merge: (data) => this.wrapMutationBuilder(builder.merge(this.normalizeMutationData(data))),
|
|
501
490
|
output: (mode) => builder.output(mode),
|
|
502
491
|
}
|
|
@@ -515,9 +504,11 @@ export class SurrealDBService {
|
|
|
515
504
|
create: (target: unknown) => {
|
|
516
505
|
const normalizedTarget = this.normalizeCreateTarget(target)
|
|
517
506
|
const builder = normalizedTarget instanceof Table ? tx.create(normalizedTarget) : tx.create(normalizedTarget)
|
|
507
|
+
// Cast needed: SurrealDB SDK transaction builder type differs nominally from internal CreateMutationBuilder interface
|
|
518
508
|
return this.wrapCreateBuilder(builder as unknown as CreateMutationBuilder)
|
|
519
509
|
},
|
|
520
510
|
update: (target: unknown) =>
|
|
511
|
+
// Cast needed: SurrealDB SDK transaction builder type differs nominally from internal MutationBuilder interface
|
|
521
512
|
this.wrapMutationBuilder(tx.update(ensureRecordId(target as RecordIdInput)) as unknown as MutationBuilder),
|
|
522
513
|
delete: (target: unknown) => tx.delete(ensureRecordId(target as RecordIdInput)),
|
|
523
514
|
relate: (from: unknown, edgeTable: unknown, to: unknown, data?: Values<Record<string, unknown>>) =>
|
|
@@ -559,7 +550,7 @@ export class SurrealDBService {
|
|
|
559
550
|
return this.normalizeQueryRows<T>(response.result, schema)
|
|
560
551
|
})
|
|
561
552
|
} catch (error) {
|
|
562
|
-
this.toSurrealError(error, queryText)
|
|
553
|
+
return this.toSurrealError(error, queryText)
|
|
563
554
|
}
|
|
564
555
|
}
|
|
565
556
|
|
|
@@ -607,7 +598,7 @@ export class SurrealDBService {
|
|
|
607
598
|
const first = rows.at(0)
|
|
608
599
|
return first ? schema.parse(first) : null
|
|
609
600
|
} catch (error) {
|
|
610
|
-
this.toSurrealError(error, `SELECT * FROM ${table} LIMIT 1`)
|
|
601
|
+
return this.toSurrealError(error, `SELECT * FROM ${table} LIMIT 1`)
|
|
611
602
|
}
|
|
612
603
|
}
|
|
613
604
|
|
|
@@ -659,7 +650,7 @@ export class SurrealDBService {
|
|
|
659
650
|
const rows = await query
|
|
660
651
|
return rows.map((row) => schema.parse(row))
|
|
661
652
|
} catch (error) {
|
|
662
|
-
this.toSurrealError(error, `SELECT * FROM ${table}`)
|
|
653
|
+
return this.toSurrealError(error, `SELECT * FROM ${table}`)
|
|
663
654
|
}
|
|
664
655
|
}
|
|
665
656
|
|
|
@@ -688,7 +679,7 @@ export class SurrealDBService {
|
|
|
688
679
|
|
|
689
680
|
return schema.parse(first)
|
|
690
681
|
} catch (error) {
|
|
691
|
-
this.toSurrealError(error, `CREATE ${table}`)
|
|
682
|
+
return this.toSurrealError(error, `CREATE ${table}`)
|
|
692
683
|
}
|
|
693
684
|
}
|
|
694
685
|
|
|
@@ -710,7 +701,7 @@ export class SurrealDBService {
|
|
|
710
701
|
const created = await client.create<unknown>(recordId).content(this.normalizeMutationData(data)).output('after')
|
|
711
702
|
return schema.parse(created)
|
|
712
703
|
} catch (error) {
|
|
713
|
-
this.toSurrealError(error, `CREATE ${recordId.toString()}`)
|
|
704
|
+
return this.toSurrealError(error, `CREATE ${recordId.toString()}`)
|
|
714
705
|
}
|
|
715
706
|
}
|
|
716
707
|
|
|
@@ -719,7 +710,7 @@ export class SurrealDBService {
|
|
|
719
710
|
id: unknown,
|
|
720
711
|
data: Record<string, unknown>,
|
|
721
712
|
schema: T,
|
|
722
|
-
options?: { mutation?:
|
|
713
|
+
options?: { mutation?: RecordMutation },
|
|
723
714
|
): Promise<z.infer<T> | null> {
|
|
724
715
|
const recordId = this.normalizeRecordId(id, table)
|
|
725
716
|
|
|
@@ -737,7 +728,7 @@ export class SurrealDBService {
|
|
|
737
728
|
const updated = await configured.output('after')
|
|
738
729
|
return updated ? schema.parse(updated) : null
|
|
739
730
|
} catch (error) {
|
|
740
|
-
this.toSurrealError(error, `UPDATE ${recordId.toString()}`)
|
|
731
|
+
return this.toSurrealError(error, `UPDATE ${recordId.toString()}`)
|
|
741
732
|
}
|
|
742
733
|
}
|
|
743
734
|
|
|
@@ -746,7 +737,7 @@ export class SurrealDBService {
|
|
|
746
737
|
id: unknown,
|
|
747
738
|
data: Record<string, unknown>,
|
|
748
739
|
schema: T,
|
|
749
|
-
options?: { mutation?:
|
|
740
|
+
options?: { mutation?: RecordMutation },
|
|
750
741
|
): Promise<z.infer<T>> {
|
|
751
742
|
const recordId = this.normalizeRecordId(id, table)
|
|
752
743
|
const keys = Object.keys(data)
|
|
@@ -766,7 +757,7 @@ export class SurrealDBService {
|
|
|
766
757
|
}
|
|
767
758
|
return schema.parse(upserted)
|
|
768
759
|
} catch (error) {
|
|
769
|
-
this.toSurrealError(error, `UPSERT ${recordId.toString()}`)
|
|
760
|
+
return this.toSurrealError(error, `UPSERT ${recordId.toString()}`)
|
|
770
761
|
}
|
|
771
762
|
}
|
|
772
763
|
|
|
@@ -782,7 +773,7 @@ export class SurrealDBService {
|
|
|
782
773
|
await client.delete<unknown>(recordId).output('before')
|
|
783
774
|
return true
|
|
784
775
|
} catch (error) {
|
|
785
|
-
this.toSurrealError(error, `DELETE ${recordId.toString()}`)
|
|
776
|
+
return this.toSurrealError(error, `DELETE ${recordId.toString()}`)
|
|
786
777
|
}
|
|
787
778
|
}
|
|
788
779
|
|
|
@@ -812,7 +803,7 @@ export class SurrealDBService {
|
|
|
812
803
|
|
|
813
804
|
return matched.length
|
|
814
805
|
} catch (error) {
|
|
815
|
-
this.toSurrealError(error, `DELETE ${table} WHERE ...`)
|
|
806
|
+
return this.toSurrealError(error, `DELETE ${table} WHERE ...`)
|
|
816
807
|
}
|
|
817
808
|
}
|
|
818
809
|
|
|
@@ -838,7 +829,7 @@ export class SurrealDBService {
|
|
|
838
829
|
}
|
|
839
830
|
return 1
|
|
840
831
|
} catch (error) {
|
|
841
|
-
this.toSurrealError(error, `UPDATE ${table} WHERE ...`)
|
|
832
|
+
return this.toSurrealError(error, `UPDATE ${table} WHERE ...`)
|
|
842
833
|
}
|
|
843
834
|
}
|
|
844
835
|
|
|
@@ -857,7 +848,7 @@ export class SurrealDBService {
|
|
|
857
848
|
.output('after')
|
|
858
849
|
return inserted as T[]
|
|
859
850
|
} catch (error) {
|
|
860
|
-
this.toSurrealError(error, `INSERT ${table}`)
|
|
851
|
+
return this.toSurrealError(error, `INSERT ${table}`)
|
|
861
852
|
}
|
|
862
853
|
}
|
|
863
854
|
|
|
@@ -887,7 +878,7 @@ export class SurrealDBService {
|
|
|
887
878
|
}
|
|
888
879
|
return related
|
|
889
880
|
} catch (error) {
|
|
890
|
-
this.toSurrealError(error, `RELATE ${fromRef.toString()}->${edgeTable}->${toRef.toString()}`)
|
|
881
|
+
return this.toSurrealError(error, `RELATE ${fromRef.toString()}->${edgeTable}->${toRef.toString()}`)
|
|
891
882
|
}
|
|
892
883
|
}
|
|
893
884
|
|
|
@@ -896,7 +887,7 @@ export class SurrealDBService {
|
|
|
896
887
|
try {
|
|
897
888
|
return this.wrapTransaction(await client.beginTransaction())
|
|
898
889
|
} catch (error) {
|
|
899
|
-
this.toSurrealError(error, 'BEGIN TRANSACTION')
|
|
890
|
+
return this.toSurrealError(error, 'BEGIN TRANSACTION')
|
|
900
891
|
}
|
|
901
892
|
}
|
|
902
893
|
|
|
@@ -945,10 +936,13 @@ export const databaseService = new Proxy({} as SurrealDBService, {
|
|
|
945
936
|
return databaseServiceOverrides.get(property)
|
|
946
937
|
}
|
|
947
938
|
|
|
948
|
-
const
|
|
949
|
-
const value =
|
|
939
|
+
const resolved = resolveConfiguredDatabaseService()
|
|
940
|
+
const value: unknown = Reflect.get(resolved, property)
|
|
950
941
|
if (typeof value === 'function') {
|
|
951
|
-
return bindTargetMethod(
|
|
942
|
+
return bindTargetMethod(
|
|
943
|
+
resolved as unknown as Record<PropertyKey, unknown>,
|
|
944
|
+
value as (...args: unknown[]) => unknown,
|
|
945
|
+
)
|
|
952
946
|
}
|
|
953
947
|
|
|
954
948
|
return value
|
package/src/db/startup.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BoundQuery, RecordId } from 'surrealdb'
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { getErrorMessage } from '../utils/errors'
|
|
5
|
+
import type { SurrealDBService, SurrealDatabaseLogger } from './service'
|
|
6
|
+
import { TABLES } from './tables'
|
|
7
7
|
|
|
8
8
|
const DATABASE_BOOTSTRAP_KEY = 'database-schema-ready'
|
|
9
9
|
const DEFAULT_RETRY_DELAY_MS = 1_000
|
package/src/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
attachWorkerEvents,
|
|
13
13
|
createTracedWorkerProcessor,
|
|
14
14
|
createWorkerShutdown,
|
|
15
|
+
DEFAULT_JOB_RETENTION,
|
|
15
16
|
registerShutdownSignals,
|
|
16
17
|
} from '../workers/worker-utils'
|
|
17
18
|
import type { WorkerHandle } from '../workers/worker-utils'
|
|
@@ -29,12 +30,7 @@ function getContextCompactionQueue(): Queue<ContextCompactionJob> {
|
|
|
29
30
|
if (!_contextCompactionQueue) {
|
|
30
31
|
_contextCompactionQueue = new Queue<ContextCompactionJob>(CONTEXT_COMPACTION_QUEUE, {
|
|
31
32
|
connection: getRedisConnectionForBullMQ(),
|
|
32
|
-
defaultJobOptions: {
|
|
33
|
-
removeOnComplete: 200,
|
|
34
|
-
removeOnFail: 200,
|
|
35
|
-
attempts: 2,
|
|
36
|
-
backoff: { type: 'exponential', delay: 3_000 },
|
|
37
|
-
},
|
|
33
|
+
defaultJobOptions: { ...DEFAULT_JOB_RETENTION, attempts: 2, backoff: { type: 'exponential', delay: 3_000 } },
|
|
38
34
|
})
|
|
39
35
|
}
|
|
40
36
|
return _contextCompactionQueue
|
|
@@ -51,11 +47,11 @@ async function processContextCompactionJob(job: Job<ContextCompactionJob>): Prom
|
|
|
51
47
|
|
|
52
48
|
const { entityId, contextSize } = job.data
|
|
53
49
|
const workstreamRef = ensureRecordId(entityId, TABLES.WORKSTREAM)
|
|
54
|
-
await workstreamService.
|
|
50
|
+
await workstreamService.markCompacting(workstreamRef)
|
|
55
51
|
try {
|
|
56
52
|
await contextCompactionService.compactWorkstreamHistory({ workstreamId: workstreamRef, contextSize })
|
|
57
53
|
} finally {
|
|
58
|
-
await workstreamService.
|
|
54
|
+
await workstreamService.clearCompacting(workstreamRef)
|
|
59
55
|
}
|
|
60
56
|
}
|
|
61
57
|
|
|
@@ -4,7 +4,12 @@ import { Queue, Worker } from 'bullmq'
|
|
|
4
4
|
import type IORedis from 'ioredis'
|
|
5
5
|
|
|
6
6
|
import type { chatLogger } from '../config/logger'
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
attachWorkerEvents,
|
|
9
|
+
createWorkerShutdown,
|
|
10
|
+
DEFAULT_JOB_RETENTION,
|
|
11
|
+
registerShutdownSignals,
|
|
12
|
+
} from '../workers/worker-utils'
|
|
8
13
|
import type { WorkerHandle } from '../workers/worker-utils'
|
|
9
14
|
|
|
10
15
|
export type DocumentSourceChannel = string
|
|
@@ -82,12 +87,7 @@ export function createDocumentProcessorQueueRuntime<TJob extends DocumentProcess
|
|
|
82
87
|
|
|
83
88
|
queue = new Queue<TJob, unknown, string>(queueName, {
|
|
84
89
|
connection: params.getConnectionForBullMQ(),
|
|
85
|
-
defaultJobOptions: {
|
|
86
|
-
removeOnComplete: 200,
|
|
87
|
-
removeOnFail: 200,
|
|
88
|
-
attempts: 3,
|
|
89
|
-
backoff: { type: 'exponential', delay: 1000 },
|
|
90
|
-
},
|
|
90
|
+
defaultJobOptions: { ...DEFAULT_JOB_RETENTION, attempts: 3, backoff: { type: 'exponential', delay: 1000 } },
|
|
91
91
|
})
|
|
92
92
|
|
|
93
93
|
return queue
|
|
@@ -5,6 +5,8 @@ import { getRedisConnectionForBullMQ } from '../redis'
|
|
|
5
5
|
import {
|
|
6
6
|
attachWorkerEvents,
|
|
7
7
|
getWorkerPath,
|
|
8
|
+
LONG_JOB_LOCK_DURATION_MS,
|
|
9
|
+
LOW_JOB_RETENTION,
|
|
8
10
|
createWorkerShutdown,
|
|
9
11
|
registerShutdownSignals,
|
|
10
12
|
} from '../workers/worker-utils'
|
|
@@ -15,18 +17,15 @@ export interface MemoryConsolidationJob {
|
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
const MEMORY_CONSOLIDATION_QUEUE = 'memory-consolidation'
|
|
20
|
+
const MEMORY_CONSOLIDATION_INTERVAL_MS = 24 * 60 * 60 * 1000
|
|
21
|
+
const MEMORY_CONSOLIDATION_JOB_ID = 'memory-consolidation-recurring'
|
|
18
22
|
|
|
19
23
|
let _memoryConsolidationQueue: Queue<MemoryConsolidationJob> | null = null
|
|
20
24
|
function getMemoryConsolidationQueue(): Queue<MemoryConsolidationJob> {
|
|
21
25
|
if (!_memoryConsolidationQueue) {
|
|
22
26
|
_memoryConsolidationQueue = new Queue<MemoryConsolidationJob>(MEMORY_CONSOLIDATION_QUEUE, {
|
|
23
27
|
connection: getRedisConnectionForBullMQ(),
|
|
24
|
-
defaultJobOptions: {
|
|
25
|
-
removeOnComplete: 50,
|
|
26
|
-
removeOnFail: 50,
|
|
27
|
-
attempts: 2,
|
|
28
|
-
backoff: { type: 'exponential', delay: 5000 },
|
|
29
|
-
},
|
|
28
|
+
defaultJobOptions: { ...LOW_JOB_RETENTION, attempts: 2, backoff: { type: 'exponential', delay: 5000 } },
|
|
30
29
|
})
|
|
31
30
|
}
|
|
32
31
|
return _memoryConsolidationQueue
|
|
@@ -42,7 +41,7 @@ export async function scheduleRecurringConsolidation() {
|
|
|
42
41
|
await getMemoryConsolidationQueue().add(
|
|
43
42
|
'consolidate',
|
|
44
43
|
{},
|
|
45
|
-
{ repeat: { every:
|
|
44
|
+
{ repeat: { every: MEMORY_CONSOLIDATION_INTERVAL_MS }, jobId: MEMORY_CONSOLIDATION_JOB_ID },
|
|
46
45
|
)
|
|
47
46
|
}
|
|
48
47
|
|
|
@@ -51,7 +50,7 @@ export function startMemoryConsolidationWorker(options: { registerSignals?: bool
|
|
|
51
50
|
const processorPath = getWorkerPath('memory-consolidation.worker.ts')
|
|
52
51
|
const worker = new Worker(MEMORY_CONSOLIDATION_QUEUE, processorPath, {
|
|
53
52
|
connection: getRedisConnectionForBullMQ(),
|
|
54
|
-
lockDuration:
|
|
53
|
+
lockDuration: LONG_JOB_LOCK_DURATION_MS,
|
|
55
54
|
concurrency: 1,
|
|
56
55
|
})
|
|
57
56
|
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
attachWorkerEvents,
|
|
11
11
|
createTracedWorkerProcessor,
|
|
12
12
|
createWorkerShutdown,
|
|
13
|
+
DEFAULT_JOB_RETENTION,
|
|
13
14
|
registerShutdownSignals,
|
|
14
15
|
} from '../workers/worker-utils'
|
|
15
16
|
import type { WorkerHandle } from '../workers/worker-utils'
|
|
@@ -43,12 +44,7 @@ function getPostChatMemoryQueue(): Queue<PostChatMemoryExtractionJob> {
|
|
|
43
44
|
if (!_postChatMemoryQueue) {
|
|
44
45
|
_postChatMemoryQueue = new Queue<PostChatMemoryExtractionJob>(POST_CHAT_MEMORY_QUEUE, {
|
|
45
46
|
connection: getRedisConnectionForBullMQ(),
|
|
46
|
-
defaultJobOptions: {
|
|
47
|
-
removeOnComplete: 200,
|
|
48
|
-
removeOnFail: 200,
|
|
49
|
-
attempts: 3,
|
|
50
|
-
backoff: { type: 'exponential', delay: 2_000 },
|
|
51
|
-
},
|
|
47
|
+
defaultJobOptions: { ...DEFAULT_JOB_RETENTION, attempts: 3, backoff: { type: 'exponential', delay: 2_000 } },
|
|
52
48
|
})
|
|
53
49
|
}
|
|
54
50
|
return _postChatMemoryQueue
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
attachWorkerEvents,
|
|
10
10
|
createTracedWorkerProcessor,
|
|
11
11
|
createWorkerShutdown,
|
|
12
|
+
DEFAULT_JOB_RETENTION,
|
|
12
13
|
registerShutdownSignals,
|
|
13
14
|
} from '../workers/worker-utils'
|
|
14
15
|
import type { WorkerHandle } from '../workers/worker-utils'
|
|
@@ -26,12 +27,7 @@ function getRecentActivityTitleRefinementQueue(): Queue<RecentActivityTitleRefin
|
|
|
26
27
|
RECENT_ACTIVITY_TITLE_REFINEMENT_QUEUE,
|
|
27
28
|
{
|
|
28
29
|
connection: getRedisConnectionForBullMQ(),
|
|
29
|
-
defaultJobOptions: {
|
|
30
|
-
removeOnComplete: 200,
|
|
31
|
-
removeOnFail: 200,
|
|
32
|
-
attempts: 3,
|
|
33
|
-
backoff: { type: 'exponential', delay: 2_000 },
|
|
34
|
-
},
|
|
30
|
+
defaultJobOptions: { ...DEFAULT_JOB_RETENTION, attempts: 3, backoff: { type: 'exponential', delay: 2_000 } },
|
|
35
31
|
},
|
|
36
32
|
)
|
|
37
33
|
}
|
|
@@ -4,7 +4,9 @@ import { serverLogger } from '../config/logger'
|
|
|
4
4
|
import { getRedisConnectionForBullMQ } from '../redis'
|
|
5
5
|
import {
|
|
6
6
|
attachWorkerEvents,
|
|
7
|
+
DEFAULT_JOB_RETENTION,
|
|
7
8
|
getWorkerPath,
|
|
9
|
+
LONG_JOB_LOCK_DURATION_MS,
|
|
8
10
|
createWorkerShutdown,
|
|
9
11
|
registerShutdownSignals,
|
|
10
12
|
} from '../workers/worker-utils'
|
|
@@ -25,12 +27,7 @@ function getRegularChatMemoryDigestQueue(): Queue<RegularChatMemoryDigestJob> {
|
|
|
25
27
|
if (!_regularChatMemoryDigestQueue) {
|
|
26
28
|
_regularChatMemoryDigestQueue = new Queue<RegularChatMemoryDigestJob>(REGULAR_CHAT_MEMORY_DIGEST_QUEUE, {
|
|
27
29
|
connection: getRedisConnectionForBullMQ(),
|
|
28
|
-
defaultJobOptions: {
|
|
29
|
-
removeOnComplete: 200,
|
|
30
|
-
removeOnFail: 200,
|
|
31
|
-
attempts: 2,
|
|
32
|
-
backoff: { type: 'exponential', delay: 5000 },
|
|
33
|
-
},
|
|
30
|
+
defaultJobOptions: { ...DEFAULT_JOB_RETENTION, attempts: 2, backoff: { type: 'exponential', delay: 5000 } },
|
|
34
31
|
})
|
|
35
32
|
}
|
|
36
33
|
return _regularChatMemoryDigestQueue
|
|
@@ -54,7 +51,7 @@ export function startRegularChatMemoryDigestWorker(options: { registerSignals?:
|
|
|
54
51
|
const worker = new Worker(REGULAR_CHAT_MEMORY_DIGEST_QUEUE, processorPath, {
|
|
55
52
|
connection: getRedisConnectionForBullMQ(),
|
|
56
53
|
concurrency: 1,
|
|
57
|
-
lockDuration:
|
|
54
|
+
lockDuration: LONG_JOB_LOCK_DURATION_MS,
|
|
58
55
|
})
|
|
59
56
|
|
|
60
57
|
attachWorkerEvents(worker, 'Regular chat memory digest', serverLogger)
|
|
@@ -4,7 +4,9 @@ import { serverLogger } from '../config/logger'
|
|
|
4
4
|
import { getRedisConnectionForBullMQ } from '../redis'
|
|
5
5
|
import {
|
|
6
6
|
attachWorkerEvents,
|
|
7
|
+
DEFAULT_JOB_RETENTION,
|
|
7
8
|
getWorkerPath,
|
|
9
|
+
LONG_JOB_LOCK_DURATION_MS,
|
|
8
10
|
createWorkerShutdown,
|
|
9
11
|
registerShutdownSignals,
|
|
10
12
|
} from '../workers/worker-utils'
|
|
@@ -22,12 +24,7 @@ function getSkillExtractionQueue(): Queue<SkillExtractionJob> {
|
|
|
22
24
|
if (!_skillExtractionQueue) {
|
|
23
25
|
_skillExtractionQueue = new Queue<SkillExtractionJob>(SKILL_EXTRACTION_QUEUE, {
|
|
24
26
|
connection: getRedisConnectionForBullMQ(),
|
|
25
|
-
defaultJobOptions: {
|
|
26
|
-
removeOnComplete: 200,
|
|
27
|
-
removeOnFail: 200,
|
|
28
|
-
attempts: 2,
|
|
29
|
-
backoff: { type: 'exponential', delay: 5000 },
|
|
30
|
-
},
|
|
27
|
+
defaultJobOptions: { ...DEFAULT_JOB_RETENTION, attempts: 2, backoff: { type: 'exponential', delay: 5000 } },
|
|
31
28
|
})
|
|
32
29
|
}
|
|
33
30
|
return _skillExtractionQueue
|
|
@@ -43,7 +40,7 @@ export function startSkillExtractionWorker(options: { registerSignals?: boolean
|
|
|
43
40
|
const worker = new Worker(SKILL_EXTRACTION_QUEUE, processorPath, {
|
|
44
41
|
connection: getRedisConnectionForBullMQ(),
|
|
45
42
|
concurrency: 1,
|
|
46
|
-
lockDuration:
|
|
43
|
+
lockDuration: LONG_JOB_LOCK_DURATION_MS,
|
|
47
44
|
})
|
|
48
45
|
|
|
49
46
|
attachWorkerEvents(worker, 'Skill extraction', serverLogger)
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
attachWorkerEvents,
|
|
11
11
|
createTracedWorkerProcessor,
|
|
12
12
|
createWorkerShutdown,
|
|
13
|
+
DEFAULT_JOB_RETENTION,
|
|
13
14
|
registerShutdownSignals,
|
|
14
15
|
} from '../workers/worker-utils'
|
|
15
16
|
import type { WorkerHandle } from '../workers/worker-utils'
|
|
@@ -26,12 +27,7 @@ function getWorkstreamTitleGenerationQueue(): Queue<WorkstreamTitleGenerationJob
|
|
|
26
27
|
if (!_workstreamTitleGenerationQueue) {
|
|
27
28
|
_workstreamTitleGenerationQueue = new Queue<WorkstreamTitleGenerationJob>(WORKSTREAM_TITLE_GENERATION_QUEUE, {
|
|
28
29
|
connection: getRedisConnectionForBullMQ(),
|
|
29
|
-
defaultJobOptions: {
|
|
30
|
-
removeOnComplete: 200,
|
|
31
|
-
removeOnFail: 200,
|
|
32
|
-
attempts: 2,
|
|
33
|
-
backoff: { type: 'exponential', delay: 2_000 },
|
|
34
|
-
},
|
|
30
|
+
defaultJobOptions: { ...DEFAULT_JOB_RETENTION, attempts: 2, backoff: { type: 'exponential', delay: 2_000 } },
|
|
35
31
|
})
|
|
36
32
|
}
|
|
37
33
|
return _workstreamTitleGenerationQueue
|
package/src/redis/connection.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import IORedis from 'ioredis'
|
|
2
2
|
import type { RedisOptions } from 'ioredis'
|
|
3
3
|
|
|
4
|
-
import { getErrorMessage } from '../utils/
|
|
4
|
+
import { getErrorMessage } from '../utils/errors'
|
|
5
5
|
|
|
6
6
|
export interface RedisConnectionLogger {
|
|
7
7
|
debug?: (message: string) => void
|
|
@@ -25,20 +25,23 @@ export interface RedisConnectionManager {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const DEFAULT_HEALTH_CHECK_INTERVAL_MS = 30_000
|
|
28
|
+
const REDIS_RETRY_STEP_MS = 50
|
|
29
|
+
const REDIS_RETRY_MAX_DELAY_MS = 2000
|
|
30
|
+
const REDIS_CONNECT_TIMEOUT_MS = 10_000
|
|
28
31
|
|
|
29
32
|
export const DEFAULT_REDIS_OPTIONS: RedisOptions = {
|
|
30
33
|
maxRetriesPerRequest: null,
|
|
31
34
|
enableReadyCheck: true,
|
|
32
35
|
enableOfflineQueue: true,
|
|
33
36
|
retryStrategy: (times: number) => {
|
|
34
|
-
const delay = Math.min(times *
|
|
37
|
+
const delay = Math.min(times * REDIS_RETRY_STEP_MS, REDIS_RETRY_MAX_DELAY_MS)
|
|
35
38
|
return delay
|
|
36
39
|
},
|
|
37
40
|
reconnectOnError: (err: Error) => {
|
|
38
41
|
const targetErrors = ['READONLY', 'ETIMEDOUT', 'ECONNREFUSED', 'EAI_AGAIN']
|
|
39
42
|
return targetErrors.some((candidate) => err.message.includes(candidate))
|
|
40
43
|
},
|
|
41
|
-
connectTimeout:
|
|
44
|
+
connectTimeout: REDIS_CONNECT_TIMEOUT_MS,
|
|
42
45
|
lazyConnect: false,
|
|
43
46
|
}
|
|
44
47
|
|
package/src/redis/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ export {
|
|
|
8
8
|
type RedisConnectionAccessor,
|
|
9
9
|
} from './connection-accessor'
|
|
10
10
|
export { withOrgMemoryLock } from './org-memory-lock'
|
|
11
|
+
export { LeaseLockLostError, withRedisLeaseLock } from './redis-lease-lock'
|
|
11
12
|
export { createWorkstreamResumableContext } from './stream-context'
|
|
12
13
|
|
|
13
14
|
export { createRedisConnectionManager }
|
|
@@ -9,7 +9,7 @@ const ORG_MEMORY_LOCK_REFRESH_INTERVAL_MS = 30_000
|
|
|
9
9
|
const ORG_MEMORY_LOCK_WAIT_LOG_INTERVAL_MS = 30_000
|
|
10
10
|
const ORG_MEMORY_LOCK_MAX_WAIT_MS = 15 * 60 * 1000
|
|
11
11
|
|
|
12
|
-
export async function withOrgMemoryLock<T>(orgId: string, fn: () => Promise<T>): Promise<T> {
|
|
12
|
+
export async function withOrgMemoryLock<T>(orgId: string, fn: (signal: AbortSignal) => Promise<T>): Promise<T> {
|
|
13
13
|
const normalizedOrgId = orgId.trim()
|
|
14
14
|
|
|
15
15
|
if (!normalizedOrgId) {
|