@lota-sdk/core 0.1.15 → 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 (138) 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 +8 -7
  9. package/src/ai/definitions.ts +80 -2
  10. package/src/ai/index.ts +0 -2
  11. package/src/bifrost/bifrost.ts +2 -7
  12. package/src/config/agent-defaults.ts +31 -21
  13. package/src/config/agent-types.ts +11 -0
  14. package/src/config/constants.ts +2 -14
  15. package/src/config/debug-logger.ts +5 -1
  16. package/src/config/index.ts +3 -0
  17. package/src/config/model-constants.ts +16 -34
  18. package/src/config/search.ts +1 -15
  19. package/src/create-runtime.ts +244 -178
  20. package/src/db/cursor-pagination.ts +3 -6
  21. package/src/db/index.ts +2 -0
  22. package/src/db/memory-store.rows.ts +7 -7
  23. package/src/db/memory-store.ts +14 -18
  24. package/src/db/memory.ts +13 -13
  25. package/src/db/service.ts +153 -79
  26. package/src/db/startup.ts +6 -10
  27. package/src/db/surreal-mutation.ts +43 -0
  28. package/src/db/tables.ts +7 -0
  29. package/src/db/workstream-message-row.ts +15 -0
  30. package/src/embeddings/provider.ts +1 -1
  31. package/src/queues/context-compaction.queue.ts +15 -46
  32. package/src/queues/delayed-node-promotion.queue.ts +41 -0
  33. package/src/queues/index.ts +3 -0
  34. package/src/queues/memory-consolidation.queue.ts +16 -51
  35. package/src/queues/plan-scheduler.queue.ts +97 -0
  36. package/src/queues/post-chat-memory.queue.ts +15 -56
  37. package/src/queues/queue-factory.ts +100 -0
  38. package/src/queues/recent-activity-title-refinement.queue.ts +15 -50
  39. package/src/queues/regular-chat-memory-digest.queue.ts +16 -52
  40. package/src/queues/skill-extraction.queue.ts +15 -47
  41. package/src/queues/workstream-title-generation.queue.ts +15 -47
  42. package/src/redis/connection.ts +6 -0
  43. package/src/redis/index.ts +1 -1
  44. package/src/redis/stream-context.ts +11 -0
  45. package/src/runtime/agent-runtime-policy.ts +106 -21
  46. package/src/runtime/approval-continuation.ts +12 -6
  47. package/src/runtime/context-compaction-runtime.ts +1 -1
  48. package/src/runtime/context-compaction.ts +22 -60
  49. package/src/runtime/execution-plan.ts +22 -18
  50. package/src/runtime/graph-designer.ts +15 -0
  51. package/src/runtime/helper-model.ts +9 -197
  52. package/src/runtime/index.ts +2 -0
  53. package/src/runtime/llm-content.ts +1 -1
  54. package/src/runtime/memory-block.ts +9 -11
  55. package/src/runtime/memory-pipeline.ts +6 -9
  56. package/src/runtime/plugin-resolution.ts +35 -0
  57. package/src/runtime/plugin-types.ts +72 -0
  58. package/src/runtime/retrieval-adapters.ts +1 -1
  59. package/src/runtime/runtime-config.ts +25 -12
  60. package/src/runtime/runtime-extensions.ts +2 -2
  61. package/src/runtime/runtime-worker-registry.ts +6 -0
  62. package/src/runtime/team-consultation-orchestrator.ts +45 -28
  63. package/src/runtime/team-consultation-prompts.ts +11 -2
  64. package/src/runtime/title-helpers.ts +2 -4
  65. package/src/runtime/workstream-chat-helpers.ts +1 -1
  66. package/src/services/adaptive-playbook.service.ts +152 -0
  67. package/src/services/agent-executor.service.ts +293 -0
  68. package/src/services/artifact-provenance.service.ts +172 -0
  69. package/src/services/attachment.service.ts +6 -11
  70. package/src/services/context-compaction.service.ts +72 -55
  71. package/src/services/context-enrichment.service.ts +33 -0
  72. package/src/services/coordination-registry.service.ts +117 -0
  73. package/src/services/document-chunk.service.ts +1 -1
  74. package/src/services/domain-agent-executor.service.ts +71 -0
  75. package/src/services/execution-plan.service.ts +269 -50
  76. package/src/services/feedback-loop.service.ts +96 -0
  77. package/src/services/global-orchestrator.service.ts +148 -0
  78. package/src/services/index.ts +26 -0
  79. package/src/services/institutional-memory.service.ts +145 -0
  80. package/src/services/learned-skill.service.ts +24 -5
  81. package/src/services/memory-assessment.service.ts +3 -2
  82. package/src/services/memory-utils.ts +3 -8
  83. package/src/services/memory.service.ts +42 -59
  84. package/src/services/monitoring-window.service.ts +86 -0
  85. package/src/services/mutating-approval.service.ts +1 -1
  86. package/src/services/node-workspace.service.ts +155 -0
  87. package/src/services/notification.service.ts +39 -0
  88. package/src/services/organization-member.service.ts +11 -4
  89. package/src/services/organization.service.ts +5 -5
  90. package/src/services/ownership-dispatcher.service.ts +403 -0
  91. package/src/services/plan-approval.service.ts +1 -1
  92. package/src/services/plan-builder.service.ts +1 -0
  93. package/src/services/plan-checkpoint.service.ts +30 -2
  94. package/src/services/plan-compiler.service.ts +5 -0
  95. package/src/services/plan-coordination.service.ts +152 -0
  96. package/src/services/plan-cycle.service.ts +284 -0
  97. package/src/services/plan-deadline.service.ts +287 -0
  98. package/src/services/plan-executor.service.ts +384 -40
  99. package/src/services/plan-run.service.ts +41 -7
  100. package/src/services/plan-scheduler.service.ts +240 -0
  101. package/src/services/plan-template.service.ts +117 -0
  102. package/src/services/plan-validator.service.ts +84 -2
  103. package/src/services/plan-workspace.service.ts +83 -0
  104. package/src/services/playbook-registry.service.ts +67 -0
  105. package/src/services/plugin-executor.service.ts +103 -0
  106. package/src/services/quality-metrics.service.ts +132 -0
  107. package/src/services/recent-activity.service.ts +27 -31
  108. package/src/services/skill-resolver.service.ts +19 -0
  109. package/src/services/system-executor.service.ts +105 -0
  110. package/src/services/workstream-message.service.ts +12 -34
  111. package/src/services/workstream-plan-registry.service.ts +22 -0
  112. package/src/services/workstream-title.service.ts +3 -1
  113. package/src/services/workstream-turn-preparation.service.ts +34 -66
  114. package/src/services/workstream.service.ts +33 -55
  115. package/src/services/workstream.types.ts +9 -9
  116. package/src/services/write-intent-validator.service.ts +81 -0
  117. package/src/storage/attachment-parser.ts +1 -1
  118. package/src/storage/attachment-utils.ts +1 -1
  119. package/src/storage/generated-document-storage.service.ts +3 -2
  120. package/src/system-agents/delegated-agent-factory.ts +2 -0
  121. package/src/tools/execution-plan.tool.ts +17 -23
  122. package/src/tools/index.ts +0 -1
  123. package/src/tools/team-think.tool.ts +6 -4
  124. package/src/utils/async.ts +2 -1
  125. package/src/utils/date-time.ts +4 -32
  126. package/src/utils/env.ts +8 -0
  127. package/src/utils/errors.ts +42 -10
  128. package/src/utils/index.ts +9 -0
  129. package/src/utils/string.ts +114 -1
  130. package/src/workers/index.ts +1 -0
  131. package/src/workers/regular-chat-memory-digest.runner.ts +2 -2
  132. package/src/workers/skill-extraction.runner.ts +1 -1
  133. package/src/workers/utils/file-section-chunker.ts +2 -1
  134. package/src/workers/utils/repomix-file-sections.ts +2 -2
  135. package/src/workers/utils/sandbox-error.ts +11 -2
  136. package/src/workers/utils/workstream-message-query.ts +11 -20
  137. package/src/workers/worker-utils.ts +2 -2
  138. package/src/tools/log-hello-world.tool.ts +0 -17
@@ -0,0 +1,86 @@
1
+ import type { MonitoringWindowConfig } from '@lota-sdk/shared'
2
+
3
+ import { planSchedulerService } from './plan-scheduler.service'
4
+
5
+ class MonitoringWindowService {
6
+ computeCheckTimes(config: MonitoringWindowConfig): { offsetMs: number; intervalMs: number }[] {
7
+ const times: { offsetMs: number; intervalMs: number }[] = []
8
+ let currentInterval = config.initialIntervalMs
9
+ let elapsed = 0
10
+ while (elapsed < config.durationMs) {
11
+ times.push({ offsetMs: elapsed, intervalMs: currentInterval })
12
+ elapsed += currentInterval
13
+ currentInterval = Math.min(currentInterval / config.decayFactor, config.maxIntervalMs)
14
+ }
15
+ return times
16
+ }
17
+
18
+ async startMonitoringWindow(params: {
19
+ runId: string
20
+ nodeId: string
21
+ config: MonitoringWindowConfig
22
+ organizationId: string
23
+ workstreamId: string
24
+ }): Promise<void> {
25
+ await planSchedulerService.createSchedule({
26
+ organizationId: params.organizationId,
27
+ workstreamId: params.workstreamId,
28
+ runId: params.runId,
29
+ nodeId: params.nodeId,
30
+ scheduleSpec: {
31
+ type: 'monitoring',
32
+ intervalMs: params.config.initialIntervalMs,
33
+ maxFires: this.computeCheckTimes(params.config).length,
34
+ missedPolicy: 'skip',
35
+ },
36
+ })
37
+ }
38
+
39
+ evaluateTermination(params: { samples: unknown[]; config: MonitoringWindowConfig }): boolean {
40
+ if (!params.config.earlyTermination) return false
41
+ if (params.samples.length < params.config.earlyTermination.minSamples) return false
42
+
43
+ const { condition, minSamples } = params.config.earlyTermination
44
+ const numericSamples = params.samples
45
+ .map((s) =>
46
+ typeof s === 'number'
47
+ ? s
48
+ : typeof s === 'object' && s !== null && 'value' in s
49
+ ? Number((s as Record<string, unknown>).value)
50
+ : NaN,
51
+ )
52
+ .filter((n) => !isNaN(n))
53
+
54
+ // No numeric data to evaluate against — don't terminate
55
+ if (numericSamples.length === 0) return false
56
+
57
+ if (condition === 'stable') {
58
+ const recent = numericSamples.slice(-minSamples)
59
+ if (recent.length < 2) return false
60
+ const mean = recent.reduce((a, b) => a + b, 0) / recent.length
61
+ const variance = recent.reduce((a, b) => a + (b - mean) ** 2, 0) / recent.length
62
+ const cv = mean !== 0 ? Math.sqrt(variance) / Math.abs(mean) : 0
63
+ return cv < 0.1
64
+ }
65
+
66
+ const thresholdMatch = condition.match(/^threshold:(\d+(?:\.\d+)?)$/)
67
+ if (thresholdMatch?.[1]) {
68
+ const threshold = parseFloat(thresholdMatch[1])
69
+ return numericSamples.some((v) => v >= threshold)
70
+ }
71
+
72
+ if (condition === 'trend:declining') {
73
+ const recent = numericSamples.slice(-minSamples)
74
+ return recent.length >= 2 && recent.every((v, i) => i === 0 || v <= (recent[i - 1] ?? v))
75
+ }
76
+
77
+ if (condition === 'trend:increasing') {
78
+ const recent = numericSamples.slice(-minSamples)
79
+ return recent.length >= 2 && recent.every((v, i) => i === 0 || v >= (recent[i - 1] ?? v))
80
+ }
81
+
82
+ throw new Error(`Unknown monitoring termination condition: ${condition}`)
83
+ }
84
+ }
85
+
86
+ export const monitoringWindowService = new MonitoringWindowService()
@@ -106,5 +106,5 @@ async function verifyMutatingApprovalForWorkstream(
106
106
  }
107
107
 
108
108
  export const verifyMutatingApproval: VerifyMutatingApproval = async (params) => {
109
- return await verifyMutatingApprovalForWorkstream(ensureRecordId(params.workstreamId, TABLES.WORKSTREAM), params)
109
+ return verifyMutatingApprovalForWorkstream(ensureRecordId(params.workstreamId, TABLES.WORKSTREAM), params)
110
110
  }
@@ -0,0 +1,155 @@
1
+ import type {
2
+ NodeResultQuality,
3
+ PlanArtifactSubmission,
4
+ PlanNodeResult,
5
+ PlanNodeSpec,
6
+ PlanSchemaRegistry,
7
+ WriteIntent,
8
+ WriteIntentAction,
9
+ } from '@lota-sdk/shared'
10
+
11
+ import { serverLogger } from '../config/logger'
12
+
13
+ export type WorkspaceEntryValidation = 'validated' | 'unvalidated'
14
+
15
+ export interface WorkspaceEntry {
16
+ targetPath: string
17
+ action: WriteIntentAction
18
+ payload: unknown
19
+ validation: WorkspaceEntryValidation
20
+ stagedAt: Date
21
+ }
22
+
23
+ export interface NodeWorkspace {
24
+ nodeId: string
25
+ ctx: Readonly<{
26
+ resolvedInput: Record<string, unknown>
27
+ inputArtifacts: PlanArtifactSubmission[]
28
+ schemaRegistry: PlanSchemaRegistry
29
+ nodeSpec: PlanNodeSpec
30
+ }>
31
+ work: Map<string, unknown>
32
+ sys: {
33
+ startedAt: Date
34
+ correctionCounts: Map<string, number>
35
+ writeLog: Array<{ targetPath: string; action: WriteIntentAction; timestamp: Date; result: 'accepted' | 'rejected' }>
36
+ }
37
+ deliverables: Map<string, WorkspaceEntry>
38
+ }
39
+
40
+ class NodeWorkspaceService {
41
+ initialize(params: {
42
+ nodeSpec: PlanNodeSpec
43
+ resolvedInput: Record<string, unknown>
44
+ inputArtifacts: PlanArtifactSubmission[]
45
+ schemaRegistry: PlanSchemaRegistry
46
+ }): NodeWorkspace {
47
+ const ctx = Object.freeze({
48
+ resolvedInput: params.resolvedInput,
49
+ inputArtifacts: params.inputArtifacts,
50
+ schemaRegistry: params.schemaRegistry,
51
+ nodeSpec: params.nodeSpec,
52
+ })
53
+
54
+ return {
55
+ nodeId: params.nodeSpec.id,
56
+ ctx,
57
+ work: new Map(),
58
+ sys: { startedAt: new Date(), correctionCounts: new Map(), writeLog: [] },
59
+ deliverables: new Map(),
60
+ }
61
+ }
62
+
63
+ stageWrite(workspace: NodeWorkspace, intent: WriteIntent, validation: WorkspaceEntryValidation): void {
64
+ const existing = workspace.deliverables.get(intent.targetPath)
65
+
66
+ if (intent.action === 'append' && existing && Array.isArray(existing.payload) && Array.isArray(intent.payload)) {
67
+ existing.payload = [...(existing.payload as unknown[]), ...intent.payload]
68
+ existing.action = intent.action
69
+ existing.validation = validation
70
+ existing.stagedAt = new Date()
71
+ } else {
72
+ workspace.deliverables.set(intent.targetPath, {
73
+ targetPath: intent.targetPath,
74
+ action: intent.action,
75
+ payload: intent.payload,
76
+ validation,
77
+ stagedAt: new Date(),
78
+ })
79
+ }
80
+
81
+ workspace.sys.writeLog.push({
82
+ targetPath: intent.targetPath,
83
+ action: intent.action,
84
+ timestamp: new Date(),
85
+ result: 'accepted',
86
+ })
87
+ }
88
+
89
+ finalize(workspace: NodeWorkspace): PlanNodeResult & { isComplete: boolean; quality: NodeResultQuality } {
90
+ const artifacts: PlanArtifactSubmission[] = []
91
+ let structuredOutput: Record<string, unknown> | undefined
92
+ let allValidated = true
93
+ const requiredNames = new Set(workspace.ctx.nodeSpec.deliverables.filter((d) => d.required).map((d) => d.name))
94
+ const presentRequired = new Set<string>()
95
+
96
+ for (const [targetPath, entry] of workspace.deliverables) {
97
+ if (entry.validation === 'unvalidated') {
98
+ allValidated = false
99
+ }
100
+
101
+ if (targetPath.startsWith('structuredOutput')) {
102
+ if (!structuredOutput) structuredOutput = {}
103
+ const subPath = targetPath.replace(/^structuredOutput\.?/, '')
104
+ if (subPath) {
105
+ structuredOutput[subPath] = entry.payload
106
+ } else {
107
+ structuredOutput =
108
+ typeof entry.payload === 'object' && entry.payload !== null && !Array.isArray(entry.payload)
109
+ ? (entry.payload as Record<string, unknown>)
110
+ : structuredOutput
111
+ }
112
+ continue
113
+ }
114
+
115
+ const spec = workspace.ctx.nodeSpec.deliverables.find((d) => d.name === targetPath)
116
+ if (!spec) {
117
+ serverLogger.warn`Write to unknown deliverable path "${targetPath}" in node ${workspace.nodeId} — skipping`
118
+ continue
119
+ }
120
+
121
+ if (requiredNames.has(targetPath)) {
122
+ presentRequired.add(targetPath)
123
+ }
124
+
125
+ artifacts.push({
126
+ name: spec.name,
127
+ kind: spec.kind,
128
+ pointer: `workspace://${workspace.nodeId}/${targetPath}`,
129
+ schemaRef: spec.schemaRef,
130
+ description: spec.description,
131
+ payload:
132
+ typeof entry.payload === 'string'
133
+ ? { content: entry.payload }
134
+ : typeof entry.payload === 'object' && entry.payload !== null
135
+ ? (entry.payload as Record<string, unknown> | unknown[])
136
+ : undefined,
137
+ })
138
+ }
139
+
140
+ const allRequiredPresent = requiredNames.size === presentRequired.size
141
+ const quality: NodeResultQuality = allRequiredPresent && allValidated ? 'full' : 'partial'
142
+ const isComplete = quality === 'full'
143
+
144
+ return { structuredOutput, artifacts, quality, isComplete }
145
+ }
146
+
147
+ cleanup(workspace: NodeWorkspace): void {
148
+ workspace.work.clear()
149
+ workspace.deliverables.clear()
150
+ workspace.sys.writeLog.length = 0
151
+ workspace.sys.correctionCounts.clear()
152
+ }
153
+ }
154
+
155
+ export const nodeWorkspaceService = new NodeWorkspaceService()
@@ -0,0 +1,39 @@
1
+ import type { NotificationSeverity } from '@lota-sdk/shared'
2
+
3
+ export interface NotificationPayload {
4
+ organizationId: string
5
+ workstreamId: string
6
+ runId?: string
7
+ nodeId?: string
8
+ severity: NotificationSeverity
9
+ title: string
10
+ body: string
11
+ dedupeKey?: string
12
+ metadata?: Record<string, unknown>
13
+ }
14
+
15
+ export interface NotificationService {
16
+ notify(payload: NotificationPayload): Promise<void>
17
+ remind(payload: NotificationPayload): Promise<void>
18
+ escalate(payload: NotificationPayload): Promise<void>
19
+ }
20
+
21
+ export class NoOpNotificationService implements NotificationService {
22
+ async notify(_payload: NotificationPayload): Promise<void> {}
23
+ async remind(_payload: NotificationPayload): Promise<void> {}
24
+ async escalate(_payload: NotificationPayload): Promise<void> {}
25
+ }
26
+
27
+ let _notificationService: NotificationService | null = null
28
+
29
+ export function configureNotificationService(service: NotificationService | null): void {
30
+ _notificationService = service
31
+ }
32
+
33
+ export function getNotificationService(): NotificationService {
34
+ if (_notificationService === null) {
35
+ throw new Error('Notification service is not configured.')
36
+ }
37
+
38
+ return _notificationService
39
+ }
@@ -1,4 +1,4 @@
1
- import { recordIdStringSchema } from '@lota-sdk/shared'
1
+ import { recordIdSchema, recordIdStringSchema } from '@lota-sdk/shared'
2
2
  import { z } from 'zod'
3
3
 
4
4
  import { BaseService } from '../db/base.service'
@@ -8,9 +8,9 @@ import { TABLES } from '../db/tables'
8
8
  import { toIsoDateTimeString } from '../utils/date-time'
9
9
 
10
10
  const organizationMemberRecordSchema = z.object({
11
- id: z.unknown(),
12
- in: z.unknown(),
13
- out: z.unknown(),
11
+ id: recordIdSchema,
12
+ in: recordIdSchema,
13
+ out: recordIdSchema,
14
14
  role: z.string(),
15
15
  createdAt: z.coerce.date(),
16
16
  })
@@ -91,6 +91,13 @@ class OrganizationMemberService extends BaseService<typeof organizationMemberRec
91
91
  )
92
92
  }
93
93
 
94
+ async getMembership(userId: RecordIdInput, organizationId: RecordIdInput): Promise<SdkOrganizationMember | null> {
95
+ const userRef = ensureRecordId(userId, TABLES.USER)
96
+ const organizationRef = ensureRecordId(organizationId, TABLES.ORGANIZATION)
97
+ const record = (await this.findAll({ in: userRef, out: organizationRef }, { limit: 1 })).at(0)
98
+ return record ? this.toPublic(record) : null
99
+ }
100
+
94
101
  async isMember(userId: RecordIdInput, organizationId: RecordIdInput): Promise<boolean> {
95
102
  const userRef = ensureRecordId(userId, TABLES.USER)
96
103
  const organizationRef = ensureRecordId(organizationId, TABLES.ORGANIZATION)
@@ -1,4 +1,4 @@
1
- import { recordIdStringSchema } from '@lota-sdk/shared'
1
+ import { recordIdSchema, recordIdStringSchema } from '@lota-sdk/shared'
2
2
  import { z } from 'zod'
3
3
 
4
4
  import { BaseService } from '../db/base.service'
@@ -9,12 +9,12 @@ import { TABLES } from '../db/tables'
9
9
  import { toIsoDateTimeString, toOptionalIsoDateTimeString } from '../utils/date-time'
10
10
 
11
11
  const organizationRecordSchema = z.object({
12
- id: z.unknown(),
12
+ id: recordIdSchema,
13
13
  name: z.string(),
14
- regularChatDigestLastWorkstreamCursorCreatedAt: z.union([z.date(), z.string(), z.number()]).optional(),
14
+ regularChatDigestLastWorkstreamCursorCreatedAt: z.coerce.date().optional(),
15
15
  regularChatDigestLastWorkstreamCursorId: z.string().optional(),
16
16
  skillExtractionLastCursorId: z.string().optional(),
17
- skillExtractionLastCursorCreatedAt: z.union([z.date(), z.string(), z.number()]).optional(),
17
+ skillExtractionLastCursorCreatedAt: z.coerce.date().optional(),
18
18
  createdAt: z.coerce.date(),
19
19
  updatedAt: z.coerce.date(),
20
20
  })
@@ -86,7 +86,7 @@ class OrganizationService extends BaseService<typeof organizationRecordSchema> {
86
86
  }
87
87
 
88
88
  async getOrganizationRecord(organizationId: RecordIdRef): Promise<SdkOrganizationRecord> {
89
- return await this.getById(ensureRecordId(organizationId, TABLES.ORGANIZATION))
89
+ return this.getById(ensureRecordId(organizationId, TABLES.ORGANIZATION))
90
90
  }
91
91
 
92
92
  async updateOrganization(organizationId: RecordIdInput, params: { name: string }): Promise<SdkOrganization> {