@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.
Files changed (95) hide show
  1. package/package.json +5 -5
  2. package/src/ai/embedding-cache.ts +7 -6
  3. package/src/ai/index.ts +1 -0
  4. package/src/bifrost/bifrost.ts +12 -7
  5. package/src/config/agent-defaults.ts +1 -1
  6. package/src/config/logger.ts +7 -9
  7. package/src/{runtime.ts → create-runtime.ts} +6 -6
  8. package/src/db/cursor-pagination.ts +1 -1
  9. package/src/db/memory-store.ts +10 -6
  10. package/src/db/memory.ts +6 -4
  11. package/src/db/schema-fingerprint.ts +1 -0
  12. package/src/db/service.ts +45 -51
  13. package/src/db/startup.ts +3 -3
  14. package/src/index.ts +1 -1
  15. package/src/queues/context-compaction.queue.ts +4 -8
  16. package/src/queues/document-processor.queue.ts +7 -7
  17. package/src/queues/memory-consolidation.queue.ts +7 -8
  18. package/src/queues/post-chat-memory.queue.ts +2 -6
  19. package/src/queues/recent-activity-title-refinement.queue.ts +2 -6
  20. package/src/queues/regular-chat-memory-digest.queue.ts +4 -7
  21. package/src/queues/skill-extraction.queue.ts +4 -7
  22. package/src/queues/workstream-title-generation.queue.ts +2 -6
  23. package/src/redis/connection.ts +6 -3
  24. package/src/redis/index.ts +1 -0
  25. package/src/redis/org-memory-lock.ts +1 -1
  26. package/src/redis/redis-lease-lock.ts +41 -8
  27. package/src/runtime/agent-stream-helpers.ts +2 -1
  28. package/src/runtime/context-compaction-constants.ts +1 -1
  29. package/src/runtime/context-compaction-runtime.ts +6 -4
  30. package/src/runtime/context-compaction.ts +19 -38
  31. package/src/runtime/execution-plan.ts +2 -2
  32. package/src/runtime/helper-model.ts +3 -1
  33. package/src/runtime/index.ts +12 -1
  34. package/src/runtime/memory-block.ts +3 -2
  35. package/src/runtime/memory-pipeline.ts +24 -5
  36. package/src/runtime/plugin-types.ts +1 -1
  37. package/src/runtime/runtime-extensions.ts +89 -13
  38. package/src/runtime/title-helpers.ts +11 -2
  39. package/src/runtime/workstream-chat-helpers.ts +5 -6
  40. package/src/runtime/workstream-routing-policy.ts +0 -30
  41. package/src/runtime/workstream-state.ts +17 -7
  42. package/src/services/attachment.service.ts +1 -1
  43. package/src/services/context-compaction.service.ts +3 -3
  44. package/src/services/document-chunk.service.ts +37 -32
  45. package/src/services/execution-plan.service.ts +2 -0
  46. package/src/services/learned-skill.service.ts +6 -10
  47. package/src/services/{memory.utils.ts → memory-utils.ts} +4 -8
  48. package/src/services/memory.service.ts +21 -18
  49. package/src/services/organization-member.service.ts +1 -1
  50. package/src/services/plan-artifact.service.ts +1 -0
  51. package/src/services/plan-executor.service.ts +2 -18
  52. package/src/services/plan-helpers.ts +15 -0
  53. package/src/services/plan-validator.service.ts +3 -18
  54. package/src/services/recent-activity-title.service.ts +3 -10
  55. package/src/services/recent-activity.service.ts +6 -12
  56. package/src/services/workstream-message.service.ts +26 -16
  57. package/src/services/workstream-title.service.ts +1 -9
  58. package/src/services/{workstream-turn-preparation.ts → workstream-turn-preparation.service.ts} +401 -314
  59. package/src/services/workstream-turn.ts +2 -2
  60. package/src/services/workstream.service.ts +22 -10
  61. package/src/services/workstream.types.ts +7 -16
  62. package/src/storage/attachment-storage.service.ts +4 -4
  63. package/src/storage/{attachments.utils.ts → attachment-utils.ts} +1 -4
  64. package/src/storage/index.ts +2 -2
  65. package/src/system-agents/{context-compacter.agent.ts → context-compaction.agent.ts} +4 -4
  66. package/src/system-agents/delegated-agent-factory.ts +3 -2
  67. package/src/system-agents/index.ts +8 -0
  68. package/src/system-agents/memory-reranker.agent.ts +1 -1
  69. package/src/system-agents/memory.agent.ts +1 -1
  70. package/src/system-agents/recent-activity-title-refiner.agent.ts +1 -1
  71. package/src/tools/execution-plan.tool.ts +6 -2
  72. package/src/tools/fetch-webpage.tool.ts +20 -18
  73. package/src/tools/index.ts +2 -2
  74. package/src/tools/read-file-parts.tool.ts +1 -1
  75. package/src/tools/search-web.tool.ts +18 -15
  76. package/src/tools/{search-tools.ts → search.tool.ts} +1 -1
  77. package/src/tools/team-think.tool.ts +9 -5
  78. package/src/tools/{tool-contract.ts → tool-contracts.ts} +9 -2
  79. package/src/utils/async.ts +1 -1
  80. package/src/utils/errors.ts +15 -0
  81. package/src/utils/hono-error-handler.ts +1 -2
  82. package/src/utils/index.ts +10 -2
  83. package/src/utils/string.ts +14 -0
  84. package/src/workers/bootstrap.ts +2 -2
  85. package/src/workers/memory-consolidation.worker.ts +12 -12
  86. package/src/workers/regular-chat-memory-digest.helpers.ts +2 -7
  87. package/src/workers/regular-chat-memory-digest.runner.ts +9 -103
  88. package/src/workers/skill-extraction.runner.ts +7 -101
  89. package/src/workers/utils/file-section-chunker.ts +5 -3
  90. package/src/workers/utils/workstream-message-query.ts +106 -0
  91. package/src/workers/worker-utils.ts +4 -0
  92. package/src/runtime/retrieval-pipeline.ts +0 -3
  93. package/src/utils/error.ts +0 -10
  94. /package/src/services/{context-compaction-runtime.ts → context-compaction-runtime.singleton.ts} +0 -0
  95. /package/src/storage/{attachments.types.ts → attachment-types.ts} +0 -0
@@ -1,3 +1,5 @@
1
+ import { createHash } from 'node:crypto'
2
+
1
3
  import { parseRowMetadata, toTimestamp, withCreatedAtMetadata } from '@lota-sdk/shared'
2
4
  import type { ChatMessage } from '@lota-sdk/shared'
3
5
  import { RecordId, surql } from 'surrealdb'
@@ -33,14 +35,21 @@ function toMessageId(value: string | RecordIdRef): string {
33
35
  return recordIdToString(value, TABLES.WORKSTREAM_MESSAGE)
34
36
  }
35
37
 
38
+ /**
39
+ * Builds a collision-free row id by hashing the workstream + message id pair.
40
+ * Previous implementation replaced non-alphanumeric chars with '_', which was
41
+ * lossy (e.g. "msg:foo" and "msg_foo" mapped to the same row id).
42
+ * Now uses a 32-char SHA-256 hex prefix -- short enough for ergonomic ids,
43
+ * long enough (128 bits) to make collisions negligible.
44
+ */
36
45
  function toWorkstreamMessageRowId(workstreamId: RecordIdRef, messageId: string): RecordId {
37
- const workstreamPart = recordIdToString(workstreamId, TABLES.WORKSTREAM).replace(/[^a-zA-Z0-9_-]/g, '_')
38
- const messagePart = messageId.replace(/[^a-zA-Z0-9_-]/g, '_')
39
- return new RecordId(TABLES.WORKSTREAM_MESSAGE, `${workstreamPart}__${messagePart}`)
46
+ const workstreamStr = recordIdToString(workstreamId, TABLES.WORKSTREAM)
47
+ const digest = createHash('sha256').update(`${workstreamStr}\0${messageId}`).digest('hex').slice(0, 32)
48
+ return new RecordId(TABLES.WORKSTREAM_MESSAGE, digest)
40
49
  }
41
50
 
42
51
  function toChatMessage(row: WorkstreamMessageRow): ChatMessage {
43
- const rowCreatedAt = toTimestamp(row.createdAt)
52
+ const rowCreatedAt = toTimestamp(row.createdAt) ?? Date.now()
44
53
  const metadata = withCreatedAtMetadata(parseRowMetadata(row.metadata), rowCreatedAt)
45
54
 
46
55
  return { id: row.messageId, role: row.role, parts: (row.parts ?? []) as ChatMessage['parts'], metadata }
@@ -74,16 +83,16 @@ class WorkstreamMessageService {
74
83
  async upsertMessages(params: { workstreamId: RecordIdRef; messages: ChatMessage[] }): Promise<void> {
75
84
  const workstreamId = params.workstreamId
76
85
 
77
- for (const message of params.messages) {
86
+ const upsertPromises = params.messages.map(async (message) => {
78
87
  const messageId = message.id.trim()
79
- if (!messageId) continue
88
+ if (!messageId) return
80
89
 
81
90
  const role = message.role
82
91
  const parts = Array.isArray(message.parts)
83
92
  ? message.parts.map((part) => structuredClone(part) as Record<string, unknown>)
84
93
  : []
85
94
  if (parts.length === 0) {
86
- if (role === 'assistant') continue
95
+ if (role === 'assistant') return
87
96
  throw new Error(`Refusing to persist workstream message "${messageId}" with empty parts`)
88
97
  }
89
98
  const rowId = toWorkstreamMessageRowId(workstreamId, messageId)
@@ -93,7 +102,9 @@ class WorkstreamMessageService {
93
102
  WorkstreamMessageExistingRowSchema,
94
103
  )
95
104
  const persistedCreatedAt =
96
- existingRow === null ? toTimestamp(message.metadata?.createdAt) : toTimestamp(existingRow.createdAt)
105
+ existingRow === null
106
+ ? (toTimestamp(message.metadata?.createdAt) ?? Date.now())
107
+ : (toTimestamp(existingRow.createdAt) ?? Date.now())
97
108
  const metadata = withCreatedAtMetadata({ ...message.metadata, createdAt: persistedCreatedAt })
98
109
 
99
110
  await databaseService.upsert(
@@ -105,12 +116,15 @@ class WorkstreamMessageService {
105
116
  role,
106
117
  parts,
107
118
  metadata,
108
- createdAt: existingRow ? new Date(toTimestamp(existingRow.createdAt)) : new Date(persistedCreatedAt),
119
+ createdAt: existingRow
120
+ ? new Date(toTimestamp(existingRow.createdAt) ?? Date.now())
121
+ : new Date(persistedCreatedAt),
109
122
  },
110
123
  WorkstreamMessageRowSchema,
111
124
  { mutation: 'content' },
112
125
  )
113
- }
126
+ })
127
+ await Promise.all(upsertPromises)
114
128
  }
115
129
 
116
130
  async listMessages(workstreamId: RecordIdRef): Promise<ChatMessage[]> {
@@ -151,7 +165,7 @@ class WorkstreamMessageService {
151
165
  throw new Error(`Workstream cursor message not found: ${cursorMessageId}`)
152
166
  }
153
167
 
154
- const cursorCreatedAt = new Date(toTimestamp(cursorRow.createdAt))
168
+ const cursorCreatedAt = new Date(toTimestamp(cursorRow.createdAt) ?? Date.now())
155
169
  const cursorId = toWorkstreamMessageRowId(workstreamId, cursorMessageId)
156
170
  const rows = await databaseService.query<unknown>(surql`
157
171
  SELECT * FROM workstreamMessage
@@ -195,7 +209,7 @@ class WorkstreamMessageService {
195
209
  .map((message) => ({
196
210
  id: message.id,
197
211
  role: message.role as 'user' | 'assistant',
198
- createdAt: new Date(toTimestamp(message.metadata?.createdAt)).toISOString(),
212
+ createdAt: new Date(toTimestamp(message.metadata?.createdAt) ?? Date.now()).toISOString(),
199
213
  content: message.parts
200
214
  .flatMap((part) => (part.type === 'text' && typeof part.text === 'string' ? [part.text] : []))
201
215
  .join('\n')
@@ -273,10 +287,6 @@ class WorkstreamMessageService {
273
287
  async listAllMessages(workstreamId: RecordIdRef): Promise<ChatMessage[]> {
274
288
  return await this.listMessages(workstreamId)
275
289
  }
276
-
277
- async addAttachments(): Promise<void> {
278
- // Attachments are no longer persisted via workstreamMessage service in AI SDK mode.
279
- }
280
290
  }
281
291
 
282
292
  export const workstreamMessageService = new WorkstreamMessageService()
@@ -3,21 +3,13 @@ import { WORKSTREAM } from '@lota-sdk/shared'
3
3
  import { chatLogger } from '../config/logger'
4
4
  import type { RecordIdRef } from '../db/record-id'
5
5
  import { createHelperModelRuntime } from '../runtime/helper-model'
6
- import { deriveTitle, limitTitleWords } from '../runtime/title-helpers'
6
+ import { deriveTitle, limitTitleWords, normalizeTitle } from '../runtime/title-helpers'
7
7
  import {
8
8
  createWorkstreamTitleGeneratorAgent,
9
9
  WORKSTREAM_TITLE_GENERATOR_PROMPT,
10
10
  } from '../system-agents/title-generator.agent'
11
- import { compactWhitespace } from '../utils/string'
12
11
  import { workstreamService } from './workstream.service'
13
12
 
14
- function normalizeTitle(value: string): string {
15
- const normalized = compactWhitespace(value)
16
- .replace(/^["'`]+|["'`]+$/g, '')
17
- .replace(/[.!?,;:]+$/g, '')
18
- return normalized.length <= 80 ? normalized : normalized.slice(0, 80).trim()
19
- }
20
-
21
13
  class WorkstreamTitleService {
22
14
  helperRuntime = createHelperModelRuntime()
23
15