@lota-sdk/core 0.1.24 → 0.1.25

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 (74) hide show
  1. package/package.json +2 -2
  2. package/src/ai/definitions.ts +5 -59
  3. package/src/ai-gateway/ai-gateway.ts +36 -28
  4. package/src/ai-gateway/cache-headers.ts +9 -0
  5. package/src/config/model-constants.ts +6 -2
  6. package/src/create-runtime.ts +1 -17
  7. package/src/db/memory-types.ts +13 -8
  8. package/src/db/memory.ts +74 -53
  9. package/src/queues/autonomous-job.queue.ts +1 -8
  10. package/src/queues/context-compaction.queue.ts +2 -2
  11. package/src/queues/index.ts +2 -6
  12. package/src/queues/organization-learning.queue.ts +78 -0
  13. package/src/queues/plan-agent-heartbeat.queue.ts +10 -16
  14. package/src/queues/title-generation.queue.ts +62 -0
  15. package/src/runtime/agent-prompt-context.ts +0 -18
  16. package/src/runtime/agent-runtime-policy.ts +9 -2
  17. package/src/runtime/context-compaction-constants.ts +4 -2
  18. package/src/runtime/context-compaction.ts +135 -118
  19. package/src/runtime/memory-pipeline.ts +70 -1
  20. package/src/runtime/memory-prompts-fact.ts +16 -0
  21. package/src/runtime/plugin-resolution.ts +3 -2
  22. package/src/runtime/plugin-types.ts +1 -42
  23. package/src/runtime/post-turn-side-effects.ts +212 -0
  24. package/src/runtime/runtime-config.ts +0 -13
  25. package/src/runtime/runtime-extensions.ts +10 -16
  26. package/src/runtime/runtime-worker-registry.ts +8 -19
  27. package/src/runtime/social-chat-agent-runner.ts +119 -0
  28. package/src/runtime/social-chat-history.ts +110 -0
  29. package/src/runtime/social-chat-prompts.ts +58 -0
  30. package/src/runtime/social-chat.ts +104 -340
  31. package/src/runtime/specialist-runner.ts +18 -0
  32. package/src/runtime/workstream-chat-helpers.ts +19 -0
  33. package/src/runtime/workstream-plan-turn.ts +195 -0
  34. package/src/runtime/workstream-state.ts +11 -8
  35. package/src/runtime/workstream-turn-context.ts +183 -0
  36. package/src/services/autonomous-job.service.ts +1 -8
  37. package/src/services/execution-plan.service.ts +205 -334
  38. package/src/services/index.ts +1 -4
  39. package/src/services/memory.service.ts +54 -44
  40. package/src/services/ownership-dispatcher.service.ts +2 -19
  41. package/src/services/plan-completion-side-effects.ts +80 -0
  42. package/src/services/plan-event-delivery.service.ts +1 -1
  43. package/src/services/plan-executor.service.ts +42 -190
  44. package/src/services/plan-node-spec.ts +60 -0
  45. package/src/services/plan-run-data.ts +88 -0
  46. package/src/services/plan-validator.service.ts +10 -8
  47. package/src/services/workstream-constants.ts +2 -0
  48. package/src/services/workstream-title.service.ts +1 -1
  49. package/src/services/workstream-turn-preparation.service.ts +208 -715
  50. package/src/services/workstream.service.ts +162 -192
  51. package/src/services/workstream.types.ts +12 -44
  52. package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -0
  53. package/src/tools/execution-plan.tool.ts +7 -6
  54. package/src/tools/remember-memory.tool.ts +7 -10
  55. package/src/tools/research-topic.tool.ts +1 -1
  56. package/src/tools/team-think.tool.ts +1 -1
  57. package/src/tools/user-questions.tool.ts +1 -1
  58. package/src/utils/autonomous-job-ids.ts +7 -0
  59. package/src/workers/organization-learning.worker.ts +31 -0
  60. package/src/workers/regular-chat-memory-digest.runner.ts +9 -3
  61. package/src/workers/skill-extraction.runner.ts +2 -2
  62. package/src/queues/recent-activity-title-refinement.queue.ts +0 -30
  63. package/src/queues/regular-chat-memory-digest.config.ts +0 -12
  64. package/src/queues/regular-chat-memory-digest.queue.ts +0 -34
  65. package/src/queues/skill-extraction.config.ts +0 -9
  66. package/src/queues/skill-extraction.queue.ts +0 -27
  67. package/src/queues/workstream-title-generation.queue.ts +0 -33
  68. package/src/services/context-enrichment.service.ts +0 -33
  69. package/src/services/coordination-registry.service.ts +0 -117
  70. package/src/services/domain-agent-executor.service.ts +0 -71
  71. package/src/services/memory-assessment.service.ts +0 -44
  72. package/src/services/playbook-registry.service.ts +0 -67
  73. package/src/workers/regular-chat-memory-digest.worker.ts +0 -22
  74. package/src/workers/skill-extraction.worker.ts +0 -22
@@ -4,10 +4,7 @@ export * from './agent-activity.service'
4
4
  export * from './artifact-provenance.service'
5
5
  export * from './attachment.service'
6
6
  export * from './autonomous-job.service'
7
- export * from './context-enrichment.service'
8
- export * from './coordination-registry.service'
9
7
  export * from './document-chunk.service'
10
- export * from './domain-agent-executor.service'
11
8
  export * from './execution-plan.service'
12
9
  export * from './institutional-memory.service'
13
10
  export * from './feedback-loop.service'
@@ -28,7 +25,6 @@ export * from './plan-workspace.service'
28
25
  export * from './plan-run.service'
29
26
  export * from './plan-scheduler.service'
30
27
  export * from './plan-template.service'
31
- export * from './playbook-registry.service'
32
28
  export * from './quality-metrics.service'
33
29
  export * from './queue-job.service'
34
30
  export * from './monitoring-window.service'
@@ -40,6 +36,7 @@ export * from './social-chat-history.service'
40
36
  export * from './system-executor.service'
41
37
  export * from './user.service'
42
38
  export * from './workstream-message.service'
39
+ export * from './workstream.types'
43
40
  export * from './workstream-title.service'
44
41
  export * from './workstream-turn'
45
42
  export * from './workstream-plan-registry.service'
@@ -26,8 +26,7 @@ import {
26
26
  import { getRuntimeConfig } from '../runtime/runtime-config'
27
27
  import { createMemoryRerankerAgent, MEMORY_RERANKER_PROMPT } from '../system-agents/memory-reranker.agent'
28
28
  import { createOrgMemoryAgent, ORG_MEMORY_PROMPT } from '../system-agents/memory.agent'
29
- import { compactWhitespace, truncateText } from '../utils/string'
30
- import { assessMemoryImportance, clampMemoryImportance } from './memory-assessment.service'
29
+ import { clampImportance, compactWhitespace, truncateText } from '../utils/string'
31
30
  import { formatMemoryResults, formatRerankedResults, getCandidateLimit } from './memory-utils'
32
31
 
33
32
  const ORG_MEMORY_TYPE = 'fact'
@@ -41,11 +40,13 @@ const MAX_CONVERSATION_HISTORY_MESSAGES = 24
41
40
  const MAX_CONVERSATION_MESSAGE_CHARS = 1_200
42
41
  const MAX_CONVERSATION_MEMORY_BLOCK_CHARS = 2_000
43
42
  const MAX_CONVERSATION_ATTACHMENT_CONTEXT_CHARS = 6_000
44
- const MAX_CONVERSATION_ASSESSMENT_CHARS = 7_000
45
43
  const ONBOARDING_MEMORY_MAX_FACTS = 16
46
44
  const MAX_ORG_MEMORY_CLIENTS = 128
45
+ const LOW_VALUE_MEMORY_IMPORTANCE_THRESHOLD = 0.45
47
46
  const ONBOARDING_MEMORY_EXTRACTION_PROMPT =
48
47
  'Onboarding mode is active. Extract multiple concrete startup facts from user-provided context: company mission, product capabilities, customer segments, pricing, traction, go-to-market plans, roadmap, team composition, technical stack, risks, and referenced URLs. Prefer one fact per concrete claim.'
48
+ const DIRECT_MEMORY_ASSESSMENT_PROMPT =
49
+ 'The user is submitting a direct memory candidate. Keep the wording faithful. Return one fact only when the statement is durable enough for memory; otherwise return no facts.'
49
50
 
50
51
  const helperModelRuntime = createHelperModelRuntime()
51
52
 
@@ -225,11 +226,6 @@ class MemoryService {
225
226
  return { messages, normalizedInput, sanitizedOutput }
226
227
  }
227
228
 
228
- private buildConversationAssessmentInput(messages: Message[]): string {
229
- const lines = messages.map((message, index) => `[${message.role.toUpperCase()} #${index + 1}] ${message.content}`)
230
- return this.normalizeConversationText(lines.join('\n\n'), MAX_CONVERSATION_ASSESSMENT_CHARS)
231
- }
232
-
233
229
  private async rerankCandidates(
234
230
  query: string,
235
231
  candidates: MemorySearchResult[],
@@ -307,16 +303,24 @@ class MemoryService {
307
303
  memory,
308
304
  scopeId,
309
305
  memoryType,
306
+ fastMode = true,
310
307
  }: {
311
308
  query: string
312
309
  memory: Memory
313
310
  scopeId: string
314
311
  memoryType: MemoryType
312
+ fastMode?: boolean
315
313
  }): Promise<string> {
316
314
  const limit = getRuntimeConfig().memory.searchK
317
- const candidateLimit = getCandidateLimit(limit)
315
+ const candidateLimit = fastMode ? limit : getCandidateLimit(limit)
318
316
 
319
- const candidates = await memory.searchCandidates(query, { scopeId, limit: candidateLimit, memoryType })
317
+ const candidates = await memory.searchCandidates(query, {
318
+ scopeId,
319
+ limit: candidateLimit,
320
+ memoryType,
321
+ fastMode,
322
+ includeNeighborContext: !fastMode,
323
+ })
320
324
 
321
325
  aiLogger.debug`Memory search candidates (scopeId: ${scopeId}, candidates: ${candidates.length})`
322
326
 
@@ -324,9 +328,9 @@ class MemoryService {
324
328
  return 'No stored memories.'
325
329
  }
326
330
 
327
- if (candidates.length <= limit) {
331
+ if (fastMode || candidates.length <= limit) {
328
332
  aiLogger.debug`Skipping reranking (candidates: ${candidates.length} <= limit: ${limit})`
329
- return formatMemoryResults(candidates)
333
+ return formatMemoryResults(candidates.slice(0, limit))
330
334
  }
331
335
 
332
336
  const reranked = await this.rerankCandidates(query, candidates, limit)
@@ -473,7 +477,7 @@ class MemoryService {
473
477
  orgId,
474
478
  agentName,
475
479
  query,
476
- fastMode = false,
480
+ fastMode = true,
477
481
  allowMultiScopeRerank = true,
478
482
  }: {
479
483
  orgId: string
@@ -659,6 +663,38 @@ class MemoryService {
659
663
  }
660
664
  }
661
665
 
666
+ async assessMemoryCandidate(params: {
667
+ orgId: string
668
+ content: string
669
+ }): Promise<Pick<ExtractedFact, 'classification' | 'durability' | 'importance' | 'rationale'> | null> {
670
+ const trimmed = compactWhitespace(params.content)
671
+ if (!trimmed) return null
672
+
673
+ const facts = await this.getOrgMemory(params.orgId).extractFactsFromMessages([{ role: 'user', content: trimmed }], {
674
+ maxFacts: 1,
675
+ customPrompt: DIRECT_MEMORY_ASSESSMENT_PROMPT,
676
+ })
677
+ if (facts.length === 0) return null
678
+ const fact = facts[0]
679
+
680
+ return {
681
+ classification: fact.classification,
682
+ durability: fact.durability,
683
+ importance: clampImportance(fact.importance),
684
+ rationale: fact.rationale,
685
+ }
686
+ }
687
+
688
+ private shouldSkipExtractedFacts(facts: ExtractedFact[]): boolean {
689
+ if (facts.length === 0) return true
690
+ return facts.every(
691
+ (fact) =>
692
+ fact.durability === 'ephemeral' &&
693
+ fact.importance <= LOW_VALUE_MEMORY_IMPORTANCE_THRESHOLD &&
694
+ fact.classification !== 'durable',
695
+ )
696
+ }
697
+
662
698
  async updateOrganizationMemoryById({
663
699
  orgId,
664
700
  memoryId,
@@ -761,39 +797,12 @@ class MemoryService {
761
797
  aiLogger.debug`addConversationMemories - orgId: "${orgId}", scopeId: "${orgScopeId}", sourceId: ${sourceId ?? 'none'}`
762
798
 
763
799
  const orgMemory = this.getOrgMemory(orgId)
764
- let assessedImportance: number | undefined
765
- let assessmentMetadata: Record<string, unknown> | undefined
766
-
767
- try {
768
- const assessmentInput = this.buildConversationAssessmentInput(messages)
769
- if (assessmentInput) {
770
- const assessment = await assessMemoryImportance({
771
- content: assessmentInput,
772
- targetScope: 'global',
773
- tag: 'memory-conversation-assessment',
774
- })
775
- if (assessment.classification === 'transient' && assessment.durability === 'ephemeral') {
776
- aiLogger.debug`Skipping transient conversation memory`
777
- return
778
- }
779
- assessedImportance = Math.round(clampMemoryImportance(assessment.importance) * 100) / 100
780
- assessmentMetadata = {
781
- importanceSource: 'model_assessed',
782
- durability: assessment.durability,
783
- classification: assessment.classification,
784
- rationale: assessment.rationale,
785
- }
786
- }
787
- } catch (error) {
788
- aiLogger.warn`Conversation memory assessment failed; continuing with default scoring: ${error}`
789
- }
790
800
 
791
801
  const scopes: AddOptions[] = [
792
802
  {
793
803
  scopeId: orgScopeId,
794
804
  memoryType: ORG_MEMORY_TYPE,
795
- ...(typeof assessedImportance === 'number' ? { importance: assessedImportance } : {}),
796
- metadata: { orgId, source, ...(sourceId ? { sourceId } : {}), ...sourceMetadata, ...assessmentMetadata },
805
+ metadata: { orgId, source, ...(sourceId ? { sourceId } : {}), ...sourceMetadata },
797
806
  },
798
807
  ]
799
808
 
@@ -802,7 +811,6 @@ class MemoryService {
802
811
  scopes.push({
803
812
  scopeId: agentId,
804
813
  memoryType: ORG_MEMORY_TYPE,
805
- ...(typeof assessedImportance === 'number' ? { importance: assessedImportance } : {}),
806
814
  metadata: {
807
815
  orgId,
808
816
  agentName: scopedAgentName,
@@ -810,7 +818,6 @@ class MemoryService {
810
818
  source,
811
819
  ...(sourceId ? { sourceId } : {}),
812
820
  ...sourceMetadata,
813
- ...assessmentMetadata,
814
821
  },
815
822
  })
816
823
  }
@@ -820,7 +827,10 @@ class MemoryService {
820
827
  ? { maxFacts: ONBOARDING_MEMORY_MAX_FACTS, customPrompt: ONBOARDING_MEMORY_EXTRACTION_PROMPT }
821
828
  : undefined
822
829
  const extractedFacts = await orgMemory.extractFactsFromMessages(messages, extractionConfig)
823
- if (extractedFacts.length === 0) return
830
+ if (this.shouldSkipExtractedFacts(extractedFacts)) {
831
+ aiLogger.debug`Skipping transient conversation memory`
832
+ return
833
+ }
824
834
  const preparedUpdates = await orgMemory.prepareFactsToScopes(extractedFacts, scopes)
825
835
  if (preparedUpdates.length === 0) return
826
836
 
@@ -24,7 +24,6 @@ import { TABLES } from '../db/tables'
24
24
  import { resolvePlanNodeExecutionVisibility, shouldPlanNodeUseVisibleTurn } from '../runtime/execution-plan-visibility'
25
25
  import { getRuntimeAdapters } from '../runtime/runtime-extensions'
26
26
  import { agentExecutorService } from './agent-executor.service'
27
- import { domainAgentExecutorService } from './domain-agent-executor.service'
28
27
  import { monitoringWindowService } from './monitoring-window.service'
29
28
  import { planExecutorService } from './plan-executor.service'
30
29
  import { planRunService } from './plan-run.service'
@@ -106,7 +105,7 @@ class OwnershipDispatcherService {
106
105
 
107
106
  for (const node of draft.nodes) {
108
107
  if (node.owner.executorType === 'agent') {
109
- if (!agentRoster.includes(node.owner.ref) && !domainAgentExecutorService.hasAgent(node.owner.ref)) {
108
+ if (!agentRoster.includes(node.owner.ref)) {
110
109
  issues.push({
111
110
  severity: 'blocking',
112
111
  code: 'agent_executor_missing',
@@ -264,7 +263,7 @@ class OwnershipDispatcherService {
264
263
  }
265
264
 
266
265
  private async shouldAutoDispatch(run: PlanRunRecord): Promise<boolean> {
267
- const workspaceProvider = getRuntimeAdapters().services?.workspaceProvider
266
+ const workspaceProvider = getRuntimeAdapters().workspaceProvider
268
267
  if (!workspaceProvider) {
269
268
  return true
270
269
  }
@@ -365,14 +364,6 @@ class OwnershipDispatcherService {
365
364
  }
366
365
 
367
366
  if (params.nodeSpec.owner.executorType === 'agent') {
368
- if (domainAgentExecutorService.hasAgent(params.nodeSpec.owner.ref)) {
369
- return domainAgentExecutorService.executeNode({
370
- nodeSpec: params.nodeSpec,
371
- resolvedInput: params.resolvedInput,
372
- inputArtifacts: params.inputArtifacts,
373
- context: params.context,
374
- })
375
- }
376
367
  return agentExecutorService.executeNode({
377
368
  nodeSpec: params.nodeSpec,
378
369
  resolvedInput: params.resolvedInput,
@@ -407,14 +398,6 @@ class OwnershipDispatcherService {
407
398
 
408
399
  if (resolved.executorType === 'agent') {
409
400
  const skillNodeSpec = { ...params.nodeSpec, owner: { executorType: 'agent' as const, ref: resolved.ref } }
410
- if (domainAgentExecutorService.hasAgent(resolved.ref)) {
411
- return domainAgentExecutorService.executeNode({
412
- nodeSpec: skillNodeSpec,
413
- resolvedInput: params.resolvedInput,
414
- inputArtifacts: params.inputArtifacts,
415
- context: params.context,
416
- })
417
- }
418
401
  return agentExecutorService.executeNode({
419
402
  nodeSpec: skillNodeSpec,
420
403
  resolvedInput: params.resolvedInput,
@@ -0,0 +1,80 @@
1
+ import type { PlanNodeOwner, PlanNodeType } from '@lota-sdk/shared'
2
+ import { PlanEventSchema } from '@lota-sdk/shared'
3
+
4
+ import { aiLogger } from '../config/logger'
5
+ import { ensureRecordId } from '../db/record-id'
6
+ import { databaseService } from '../db/service'
7
+ import { TABLES } from '../db/tables'
8
+ import { feedbackLoopService } from './feedback-loop.service'
9
+ import { institutionalMemoryService } from './institutional-memory.service'
10
+ import { planEventDeliveryService } from './plan-event-delivery.service'
11
+ import { planRunService } from './plan-run.service'
12
+ import type { PlanValidationIssueInput } from './plan-validator.service'
13
+ import { qualityMetricsService } from './quality-metrics.service'
14
+
15
+ export async function runPlanNodeCompletionSideEffects(params: {
16
+ runId: string
17
+ organizationId: string
18
+ nodeId: string
19
+ nodeLabel: string
20
+ nodeOwnerRef: string
21
+ nodeOwnerType: PlanNodeOwner['executorType']
22
+ nodeType: PlanNodeType
23
+ nodeStartedAt?: string | Date | null
24
+ nodeAttemptCount: number
25
+ artifactCount: number
26
+ validationIssues: PlanValidationIssueInput[]
27
+ }): Promise<void> {
28
+ const executionTimeMs = params.nodeStartedAt ? Date.now() - new Date(params.nodeStartedAt).getTime() : 0
29
+ await qualityMetricsService.recordNodeMetrics({
30
+ organizationId: params.organizationId,
31
+ runId: params.runId,
32
+ nodeId: params.nodeId,
33
+ metrics: {
34
+ executionTimeMs: Math.max(0, executionTimeMs),
35
+ attemptCount: params.nodeAttemptCount,
36
+ artifactCount: params.artifactCount,
37
+ validationIssueCount: params.validationIssues.length,
38
+ ownerRef: params.nodeOwnerRef,
39
+ ownerType: params.nodeOwnerType,
40
+ nodeType: params.nodeType,
41
+ },
42
+ })
43
+ }
44
+
45
+ async function runPlanCompletionSideEffects(params: { runId: string; organizationId: string }): Promise<void> {
46
+ await qualityMetricsService.recordCycleMetrics({ organizationId: params.organizationId, runId: params.runId })
47
+
48
+ const recommendations = await feedbackLoopService.analyzeOutcomes({
49
+ runId: params.runId,
50
+ organizationId: params.organizationId,
51
+ })
52
+ if (recommendations.length > 0) {
53
+ const run = await planRunService.getRunById(params.runId)
54
+ const specRecord = await planRunService.getPlanSpecById(run.planSpecId)
55
+ const event = await databaseService.create(
56
+ TABLES.PLAN_EVENT,
57
+ {
58
+ planSpecId: ensureRecordId(specRecord.id, TABLES.PLAN_SPEC),
59
+ runId: ensureRecordId(run.id, TABLES.PLAN_RUN),
60
+ eventType: 'feedback-analyzed',
61
+ message: `Feedback analysis produced ${recommendations.length} recommendation(s).`,
62
+ detail: { recommendations },
63
+ emittedBy: 'system',
64
+ },
65
+ PlanEventSchema,
66
+ )
67
+ await planEventDeliveryService.dispatchEvent(event)
68
+ }
69
+
70
+ await institutionalMemoryService.extractPatterns({ organizationId: params.organizationId, runId: params.runId })
71
+ }
72
+
73
+ export async function runPlanCompletionSideEffectsSafely(params: {
74
+ runId: string
75
+ organizationId: string
76
+ }): Promise<void> {
77
+ await runPlanCompletionSideEffects(params).catch((error) => {
78
+ aiLogger.warn`Plan completion side effects failed for run ${params.runId}: ${error instanceof Error ? error.message : String(error)}`
79
+ })
80
+ }
@@ -103,7 +103,7 @@ class PlanEventDeliveryService {
103
103
  }
104
104
 
105
105
  private async dispatchAdapterEvent(envelope: LotaRuntimePlanEventEnvelope): Promise<void> {
106
- const adapter = getRuntimeAdapters().events?.planEventAdapter
106
+ const adapter = getRuntimeAdapters().planEventAdapter
107
107
  if (!adapter) {
108
108
  return
109
109
  }