@lota-sdk/core 0.1.14 → 0.1.16

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 (174) hide show
  1. package/infrastructure/schema/00_identity.surql +0 -2
  2. package/infrastructure/schema/01_memory.surql +1 -1
  3. package/infrastructure/schema/02_execution_plan.surql +62 -1
  4. package/infrastructure/schema/03_learned_skill.surql +1 -1
  5. package/infrastructure/schema/06_playbook.surql +25 -0
  6. package/infrastructure/schema/07_institutional_memory.surql +13 -0
  7. package/infrastructure/schema/08_quality_metrics.surql +17 -0
  8. package/package.json +9 -8
  9. package/src/ai/definitions.ts +80 -2
  10. package/src/ai/embedding-cache.ts +7 -6
  11. package/src/ai/index.ts +0 -1
  12. package/src/bifrost/bifrost.ts +14 -14
  13. package/src/config/agent-defaults.ts +32 -22
  14. package/src/config/agent-types.ts +11 -0
  15. package/src/config/constants.ts +2 -14
  16. package/src/config/debug-logger.ts +5 -1
  17. package/src/config/index.ts +3 -0
  18. package/src/config/logger.ts +7 -9
  19. package/src/config/model-constants.ts +16 -34
  20. package/src/config/search.ts +1 -15
  21. package/src/create-runtime.ts +453 -0
  22. package/src/db/cursor-pagination.ts +3 -6
  23. package/src/db/index.ts +2 -0
  24. package/src/db/memory-store.rows.ts +7 -7
  25. package/src/db/memory-store.ts +24 -24
  26. package/src/db/memory.ts +18 -16
  27. package/src/db/schema-fingerprint.ts +1 -0
  28. package/src/db/service.ts +193 -122
  29. package/src/db/startup.ts +9 -13
  30. package/src/db/surreal-mutation.ts +43 -0
  31. package/src/db/tables.ts +7 -0
  32. package/src/db/workstream-message-row.ts +15 -0
  33. package/src/embeddings/provider.ts +1 -1
  34. package/src/index.ts +1 -1
  35. package/src/queues/context-compaction.queue.ts +17 -52
  36. package/src/queues/delayed-node-promotion.queue.ts +41 -0
  37. package/src/queues/document-processor.queue.ts +7 -7
  38. package/src/queues/index.ts +3 -0
  39. package/src/queues/memory-consolidation.queue.ts +18 -54
  40. package/src/queues/plan-scheduler.queue.ts +97 -0
  41. package/src/queues/post-chat-memory.queue.ts +15 -60
  42. package/src/queues/queue-factory.ts +100 -0
  43. package/src/queues/recent-activity-title-refinement.queue.ts +15 -54
  44. package/src/queues/regular-chat-memory-digest.queue.ts +16 -55
  45. package/src/queues/skill-extraction.queue.ts +15 -50
  46. package/src/queues/workstream-title-generation.queue.ts +15 -51
  47. package/src/redis/connection.ts +12 -3
  48. package/src/redis/index.ts +2 -1
  49. package/src/redis/org-memory-lock.ts +1 -1
  50. package/src/redis/redis-lease-lock.ts +41 -8
  51. package/src/redis/stream-context.ts +11 -0
  52. package/src/runtime/agent-runtime-policy.ts +106 -21
  53. package/src/runtime/agent-stream-helpers.ts +2 -1
  54. package/src/runtime/approval-continuation.ts +12 -6
  55. package/src/runtime/context-compaction-constants.ts +1 -1
  56. package/src/runtime/context-compaction-runtime.ts +7 -5
  57. package/src/runtime/context-compaction.ts +40 -97
  58. package/src/runtime/execution-plan.ts +23 -19
  59. package/src/runtime/graph-designer.ts +15 -0
  60. package/src/runtime/helper-model.ts +10 -196
  61. package/src/runtime/index.ts +14 -1
  62. package/src/runtime/llm-content.ts +1 -1
  63. package/src/runtime/memory-block.ts +11 -12
  64. package/src/runtime/memory-pipeline.ts +26 -10
  65. package/src/runtime/plugin-resolution.ts +35 -0
  66. package/src/runtime/plugin-types.ts +73 -1
  67. package/src/runtime/retrieval-adapters.ts +1 -1
  68. package/src/runtime/runtime-config.ts +25 -12
  69. package/src/runtime/runtime-extensions.ts +91 -15
  70. package/src/runtime/runtime-worker-registry.ts +6 -0
  71. package/src/runtime/team-consultation-orchestrator.ts +45 -28
  72. package/src/runtime/team-consultation-prompts.ts +11 -2
  73. package/src/runtime/title-helpers.ts +11 -4
  74. package/src/runtime/workstream-chat-helpers.ts +6 -7
  75. package/src/runtime/workstream-routing-policy.ts +0 -30
  76. package/src/runtime/workstream-state.ts +17 -7
  77. package/src/services/adaptive-playbook.service.ts +152 -0
  78. package/src/services/agent-executor.service.ts +293 -0
  79. package/src/services/artifact-provenance.service.ts +172 -0
  80. package/src/services/attachment.service.ts +7 -12
  81. package/src/services/context-compaction.service.ts +75 -58
  82. package/src/services/context-enrichment.service.ts +33 -0
  83. package/src/services/coordination-registry.service.ts +117 -0
  84. package/src/services/document-chunk.service.ts +38 -33
  85. package/src/services/domain-agent-executor.service.ts +71 -0
  86. package/src/services/execution-plan.service.ts +271 -50
  87. package/src/services/feedback-loop.service.ts +96 -0
  88. package/src/services/global-orchestrator.service.ts +148 -0
  89. package/src/services/index.ts +26 -0
  90. package/src/services/institutional-memory.service.ts +145 -0
  91. package/src/services/learned-skill.service.ts +30 -15
  92. package/src/services/memory-assessment.service.ts +3 -2
  93. package/src/services/{memory.utils.ts → memory-utils.ts} +4 -13
  94. package/src/services/memory.service.ts +55 -69
  95. package/src/services/monitoring-window.service.ts +86 -0
  96. package/src/services/mutating-approval.service.ts +1 -1
  97. package/src/services/node-workspace.service.ts +155 -0
  98. package/src/services/notification.service.ts +39 -0
  99. package/src/services/organization-member.service.ts +12 -5
  100. package/src/services/organization.service.ts +5 -5
  101. package/src/services/ownership-dispatcher.service.ts +403 -0
  102. package/src/services/plan-approval.service.ts +1 -1
  103. package/src/services/plan-artifact.service.ts +1 -0
  104. package/src/services/plan-builder.service.ts +1 -0
  105. package/src/services/plan-checkpoint.service.ts +30 -2
  106. package/src/services/plan-compiler.service.ts +5 -0
  107. package/src/services/plan-coordination.service.ts +152 -0
  108. package/src/services/plan-cycle.service.ts +284 -0
  109. package/src/services/plan-deadline.service.ts +287 -0
  110. package/src/services/plan-executor.service.ts +386 -58
  111. package/src/services/plan-helpers.ts +15 -0
  112. package/src/services/plan-run.service.ts +41 -7
  113. package/src/services/plan-scheduler.service.ts +240 -0
  114. package/src/services/plan-template.service.ts +117 -0
  115. package/src/services/plan-validator.service.ts +87 -20
  116. package/src/services/plan-workspace.service.ts +83 -0
  117. package/src/services/playbook-registry.service.ts +67 -0
  118. package/src/services/plugin-executor.service.ts +103 -0
  119. package/src/services/quality-metrics.service.ts +132 -0
  120. package/src/services/recent-activity-title.service.ts +3 -10
  121. package/src/services/recent-activity.service.ts +33 -43
  122. package/src/services/skill-resolver.service.ts +19 -0
  123. package/src/services/system-executor.service.ts +105 -0
  124. package/src/services/workstream-message.service.ts +29 -41
  125. package/src/services/workstream-plan-registry.service.ts +22 -0
  126. package/src/services/workstream-title.service.ts +3 -9
  127. package/src/services/{workstream-turn-preparation.ts → workstream-turn-preparation.service.ts} +428 -373
  128. package/src/services/workstream-turn.ts +2 -2
  129. package/src/services/workstream.service.ts +55 -65
  130. package/src/services/workstream.types.ts +10 -19
  131. package/src/services/write-intent-validator.service.ts +81 -0
  132. package/src/storage/attachment-parser.ts +1 -1
  133. package/src/storage/attachment-storage.service.ts +4 -4
  134. package/src/storage/{attachments.utils.ts → attachment-utils.ts} +2 -5
  135. package/src/storage/generated-document-storage.service.ts +3 -2
  136. package/src/storage/index.ts +2 -2
  137. package/src/system-agents/{context-compacter.agent.ts → context-compaction.agent.ts} +4 -4
  138. package/src/system-agents/delegated-agent-factory.ts +5 -2
  139. package/src/system-agents/index.ts +8 -0
  140. package/src/system-agents/memory-reranker.agent.ts +1 -1
  141. package/src/system-agents/memory.agent.ts +1 -1
  142. package/src/system-agents/recent-activity-title-refiner.agent.ts +1 -1
  143. package/src/tools/execution-plan.tool.ts +17 -19
  144. package/src/tools/fetch-webpage.tool.ts +20 -18
  145. package/src/tools/index.ts +2 -3
  146. package/src/tools/read-file-parts.tool.ts +1 -1
  147. package/src/tools/search-web.tool.ts +18 -15
  148. package/src/tools/{search-tools.ts → search.tool.ts} +1 -1
  149. package/src/tools/team-think.tool.ts +14 -8
  150. package/src/tools/{tool-contract.ts → tool-contracts.ts} +9 -2
  151. package/src/utils/async.ts +3 -2
  152. package/src/utils/date-time.ts +4 -32
  153. package/src/utils/env.ts +8 -0
  154. package/src/utils/errors.ts +47 -0
  155. package/src/utils/hono-error-handler.ts +1 -2
  156. package/src/utils/index.ts +19 -2
  157. package/src/utils/string.ts +128 -1
  158. package/src/workers/bootstrap.ts +2 -2
  159. package/src/workers/index.ts +1 -0
  160. package/src/workers/memory-consolidation.worker.ts +12 -12
  161. package/src/workers/regular-chat-memory-digest.helpers.ts +2 -7
  162. package/src/workers/regular-chat-memory-digest.runner.ts +11 -105
  163. package/src/workers/skill-extraction.runner.ts +8 -102
  164. package/src/workers/utils/file-section-chunker.ts +6 -3
  165. package/src/workers/utils/repomix-file-sections.ts +2 -2
  166. package/src/workers/utils/sandbox-error.ts +11 -2
  167. package/src/workers/utils/workstream-message-query.ts +97 -0
  168. package/src/workers/worker-utils.ts +6 -2
  169. package/src/runtime/retrieval-pipeline.ts +0 -3
  170. package/src/runtime.ts +0 -387
  171. package/src/tools/log-hello-world.tool.ts +0 -17
  172. package/src/utils/error.ts +0 -10
  173. /package/src/services/{context-compaction-runtime.ts → context-compaction-runtime.singleton.ts} +0 -0
  174. /package/src/storage/{attachments.types.ts → attachment-types.ts} +0 -0
@@ -0,0 +1,33 @@
1
+ import type { ContextEnrichment } from '@lota-sdk/shared'
2
+
3
+ import { serverLogger } from '../config/logger'
4
+ import { getRuntimeConfig } from '../runtime/runtime-config'
5
+
6
+ class ContextEnrichmentService {
7
+ async enrichForPlanCreation(params: { objective: string; organizationId: string }): Promise<ContextEnrichment[]> {
8
+ const pluginRuntime = getRuntimeConfig().pluginRuntime ?? {}
9
+ const enrichers = Object.values(pluginRuntime).flatMap((plugin) => plugin.contextEnrichers ?? [])
10
+
11
+ if (enrichers.length === 0) return []
12
+
13
+ const results = await Promise.allSettled(
14
+ enrichers.map((enricher) =>
15
+ enricher.enrich({ objective: params.objective, organizationId: params.organizationId }),
16
+ ),
17
+ )
18
+
19
+ const enrichments: ContextEnrichment[] = []
20
+ for (let i = 0; i < results.length; i++) {
21
+ const result = results[i]
22
+ if (result.status === 'fulfilled') {
23
+ enrichments.push({ domain: enrichers[i].domain, data: result.value.data, confidence: result.value.confidence })
24
+ } else {
25
+ serverLogger.warn`Context enricher "${enrichers[i].domain}" failed: ${result.reason}`
26
+ }
27
+ }
28
+
29
+ return enrichments.sort((a, b) => b.confidence - a.confidence)
30
+ }
31
+ }
32
+
33
+ export const contextEnrichmentService = new ContextEnrichmentService()
@@ -0,0 +1,117 @@
1
+ import type { SignalDeclaration } from '@lota-sdk/shared'
2
+
3
+ import { serverLogger } from '../config/logger'
4
+ import type { LotaPlugin } from '../runtime/plugin-types'
5
+ import { getRuntimeConfig } from '../runtime/runtime-config'
6
+ import type { PlanValidationIssueInput } from './plan-validator.service'
7
+
8
+ export interface EmittedSignal {
9
+ signal: string
10
+ payload: unknown
11
+ sourcePlugin: string
12
+ consumers: string[]
13
+ emittedAt: Date
14
+ }
15
+
16
+ const MAX_EMITTED_SIGNALS = 1000
17
+
18
+ class CoordinationRegistryService {
19
+ private producers = new Map<string, string[]>()
20
+ private consumers = new Map<string, string[]>()
21
+ private _emittedSignals: EmittedSignal[] = []
22
+
23
+ register(pluginRef: string, signals: SignalDeclaration[]): void {
24
+ for (const signal of signals) {
25
+ if (signal.direction === 'produces') {
26
+ const existing = this.producers.get(signal.signalName) ?? []
27
+ existing.push(pluginRef)
28
+ this.producers.set(signal.signalName, existing)
29
+ } else {
30
+ const existing = this.consumers.get(signal.signalName) ?? []
31
+ existing.push(pluginRef)
32
+ this.consumers.set(signal.signalName, existing)
33
+ }
34
+ }
35
+ }
36
+
37
+ async emit(signal: string, payload: unknown, sourcePlugin: string): Promise<void> {
38
+ const consumers = this.consumers.get(signal) ?? []
39
+
40
+ this._emittedSignals.push({ signal, payload, sourcePlugin, consumers: [...consumers], emittedAt: new Date() })
41
+ if (this._emittedSignals.length > MAX_EMITTED_SIGNALS) {
42
+ this._emittedSignals.shift()
43
+ }
44
+
45
+ serverLogger.debug(
46
+ `Signal "${signal}" emitted by "${sourcePlugin}" — ${consumers.length} consumer(s): ${consumers.join(', ') || 'none'}`,
47
+ )
48
+
49
+ if (consumers.length === 0) return
50
+
51
+ let pluginRuntime: Record<string, unknown> = {}
52
+ try {
53
+ pluginRuntime = getRuntimeConfig().pluginRuntime ?? {}
54
+ } catch {
55
+ // Runtime not yet configured — skip plugin dispatch
56
+ }
57
+
58
+ for (const consumerRef of consumers) {
59
+ const plugin = pluginRuntime[consumerRef] as LotaPlugin | undefined
60
+ if (plugin?.onSignal) {
61
+ try {
62
+ await plugin.onSignal(signal, payload, sourcePlugin)
63
+ } catch (error) {
64
+ serverLogger.warn`Signal handler for "${consumerRef}" failed on signal "${signal}": ${error}`
65
+ }
66
+ }
67
+ }
68
+ }
69
+
70
+ getEmittedSignals(signalName?: string): EmittedSignal[] {
71
+ if (!signalName) return [...this._emittedSignals]
72
+ return this._emittedSignals.filter((entry) => entry.signal === signalName)
73
+ }
74
+
75
+ getProducers(signalName: string): string[] {
76
+ return this.producers.get(signalName) ?? []
77
+ }
78
+
79
+ getConsumers(signalName: string): string[] {
80
+ return this.consumers.get(signalName) ?? []
81
+ }
82
+
83
+ validate(): PlanValidationIssueInput[] {
84
+ const issues: PlanValidationIssueInput[] = []
85
+
86
+ for (const [signalName] of this.producers) {
87
+ if ((this.consumers.get(signalName) ?? []).length === 0) {
88
+ issues.push({
89
+ severity: 'warning',
90
+ code: 'signal_no_consumer',
91
+ message: `Signal "${signalName}" is produced but has no consumers.`,
92
+ })
93
+ }
94
+ }
95
+
96
+ for (const [signalName] of this.consumers) {
97
+ if ((this.producers.get(signalName) ?? []).length === 0) {
98
+ issues.push({
99
+ severity: 'warning',
100
+ code: 'signal_no_producer',
101
+ message: `Signal "${signalName}" is consumed but has no producers.`,
102
+ })
103
+ }
104
+ }
105
+
106
+ return issues
107
+ }
108
+
109
+ /** Reset internal state. Intended for testing. */
110
+ _reset(): void {
111
+ this.producers.clear()
112
+ this.consumers.clear()
113
+ this._emittedSignals = []
114
+ }
115
+ }
116
+
117
+ export const coordinationRegistryService = new CoordinationRegistryService()
@@ -3,6 +3,7 @@ import { createHash } from 'node:crypto'
3
3
  import { chunkMarkdownDocument, chunkPagedDocument, chunkPlainTextDocument } from '../document/org-document-chunking'
4
4
  import type { ParsedDocumentChunk } from '../document/org-document-chunking'
5
5
  import { getDefaultEmbeddings } from '../embeddings/provider'
6
+ import { CHARS_PER_TOKEN_ESTIMATE } from '../utils/string'
6
7
 
7
8
  type DocumentChunkEmbeddings = {
8
9
  embedDocuments(documents: string[]): Promise<number[][]>
@@ -58,12 +59,14 @@ export class DocumentChunkService {
58
59
  return createHash('sha256').update(content).digest('hex')
59
60
  }
60
61
 
62
+ // Uses 4 chars/token (conservative estimate for document content which tends
63
+ // to have longer words than conversational text where 3 chars/token is used).
61
64
  estimateTokenCount(content: string): number {
62
- return Math.max(1, Math.ceil(content.length / 4))
65
+ return Math.max(1, Math.ceil(content.length / (CHARS_PER_TOKEN_ESTIMATE + 1)))
63
66
  }
64
67
 
65
68
  async embedQuery(query: string): Promise<number[]> {
66
- return await this.embeddings.embedQuery(query)
69
+ return this.embeddings.embedQuery(query)
67
70
  }
68
71
 
69
72
  async syncVersionedChunks<TRecord, TPayload>(params: {
@@ -95,37 +98,39 @@ export class DocumentChunkService {
95
98
 
96
99
  await params.archive(staleVersionRows)
97
100
 
98
- for (const [index, chunk] of params.chunks.entries()) {
99
- const contentHash = this.hashContent(chunk.content)
100
- const existingRow = existingByChunkKey.get(chunk.chunkKey)
101
- const payload = params.buildPayload({
102
- chunk,
103
- embedding: embeddings[index] ?? [],
104
- contentHash,
105
- tokenEstimate: this.estimateTokenCount(chunk.content),
106
- })
107
-
108
- seenChunkKeys.add(chunk.chunkKey)
109
-
110
- if (!existingRow) {
111
- await params.create(payload)
112
- continue
113
- }
114
-
115
- const current = params.selectShape(existingRow)
116
- const hasChanged =
117
- current.contentHash !== contentHash ||
118
- current.chunkIndex !== chunk.chunkIndex ||
119
- (current.sectionPath ?? null) !== (chunk.sectionPath ?? null) ||
120
- (current.pageStart ?? null) !== (chunk.pageStart ?? null) ||
121
- (current.pageEnd ?? null) !== (chunk.pageEnd ?? null)
122
-
123
- if (!hasChanged) {
124
- continue
125
- }
126
-
127
- await params.update(existingRow, payload)
128
- }
101
+ await Promise.all(
102
+ params.chunks.map(async (chunk, index) => {
103
+ const contentHash = this.hashContent(chunk.content)
104
+ const existingRow = existingByChunkKey.get(chunk.chunkKey)
105
+ const payload = params.buildPayload({
106
+ chunk,
107
+ embedding: embeddings[index] ?? [],
108
+ contentHash,
109
+ tokenEstimate: this.estimateTokenCount(chunk.content),
110
+ })
111
+
112
+ seenChunkKeys.add(chunk.chunkKey)
113
+
114
+ if (!existingRow) {
115
+ await params.create(payload)
116
+ return
117
+ }
118
+
119
+ const current = params.selectShape(existingRow)
120
+ const hasChanged =
121
+ current.contentHash !== contentHash ||
122
+ current.chunkIndex !== chunk.chunkIndex ||
123
+ (current.sectionPath ?? null) !== (chunk.sectionPath ?? null) ||
124
+ (current.pageStart ?? null) !== (chunk.pageStart ?? null) ||
125
+ (current.pageEnd ?? null) !== (chunk.pageEnd ?? null)
126
+
127
+ if (!hasChanged) {
128
+ return
129
+ }
130
+
131
+ await params.update(existingRow, payload)
132
+ }),
133
+ )
129
134
 
130
135
  const removedCurrentVersionRows = existingRows.filter((row) => {
131
136
  const current = params.selectShape(row)
@@ -0,0 +1,71 @@
1
+ import type { OwnershipDispatchContext, PlanArtifactSubmission, PlanNodeResult, PlanNodeSpec } from '@lota-sdk/shared'
2
+
3
+ import type { LotaPlugin, PluginDomainAgentDefinition } from '../runtime/plugin-types'
4
+ import { getRuntimeConfig } from '../runtime/runtime-config'
5
+ import type { PlanValidationIssueInput } from './plan-validator.service'
6
+
7
+ class DomainAgentExecutorService {
8
+ private registry = new Map<string, { pluginRef: string; definition: PluginDomainAgentDefinition }>()
9
+
10
+ configure(pluginRuntime: Record<string, LotaPlugin>): void {
11
+ this.registry.clear()
12
+ for (const [pluginRef, plugin] of Object.entries(pluginRuntime)) {
13
+ if (plugin.domainAgents) {
14
+ for (const def of plugin.domainAgents) {
15
+ this.registry.set(def.agentId, { pluginRef, definition: def })
16
+ }
17
+ }
18
+ }
19
+ }
20
+
21
+ hasAgent(agentId: string): boolean {
22
+ return this.registry.has(agentId)
23
+ }
24
+
25
+ validateOwner(agentId: string, nodeId: string): PlanValidationIssueInput[] {
26
+ if (!this.registry.has(agentId)) {
27
+ return [
28
+ {
29
+ severity: 'blocking',
30
+ code: 'domain_agent_missing',
31
+ message: `Node "${nodeId}" references unknown domain agent "${agentId}".`,
32
+ nodeId,
33
+ },
34
+ ]
35
+ }
36
+ return []
37
+ }
38
+
39
+ async executeNode(params: {
40
+ nodeSpec: PlanNodeSpec
41
+ resolvedInput: Record<string, unknown>
42
+ inputArtifacts: PlanArtifactSubmission[]
43
+ context: OwnershipDispatchContext
44
+ }): Promise<PlanNodeResult> {
45
+ const entry = this.registry.get(params.nodeSpec.owner.ref)
46
+ if (!entry) {
47
+ throw new Error(`Domain agent "${params.nodeSpec.owner.ref}" not registered.`)
48
+ }
49
+
50
+ const plugin = (getRuntimeConfig().pluginRuntime as Record<string, LotaPlugin | undefined> | undefined)?.[
51
+ entry.pluginRef
52
+ ]
53
+ if (!plugin?.nodeExecutor) {
54
+ throw new Error(`Plugin "${entry.pluginRef}" does not have a nodeExecutor.`)
55
+ }
56
+
57
+ return plugin.nodeExecutor.executeNode({
58
+ operation: `domain-agent:${entry.definition.agentId}`,
59
+ nodeSpec: params.nodeSpec,
60
+ inputs: params.resolvedInput,
61
+ context: {
62
+ organizationId: params.context.organizationId,
63
+ workstreamId: params.context.workstreamId,
64
+ planId: params.context.planId,
65
+ nodeId: params.context.nodeId,
66
+ },
67
+ })
68
+ }
69
+ }
70
+
71
+ export const domainAgentExecutorService = new DomainAgentExecutorService()