@lota-sdk/core 0.4.8 → 0.4.10
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 +11 -12
- package/src/ai/embedding-cache.ts +96 -22
- package/src/ai-gateway/ai-gateway.ts +766 -223
- package/src/config/agent-defaults.ts +189 -75
- package/src/config/agent-types.ts +54 -4
- package/src/config/background-processing.ts +1 -1
- package/src/config/constants.ts +8 -2
- package/src/config/index.ts +0 -1
- package/src/config/logger.ts +299 -19
- package/src/config/thread-defaults.ts +40 -20
- package/src/create-runtime.ts +200 -449
- package/src/db/base.service.ts +52 -28
- package/src/db/cursor-pagination.ts +71 -30
- package/src/db/memory-query-builder.ts +2 -1
- package/src/db/memory-store.helpers.ts +4 -7
- package/src/db/memory-store.ts +868 -601
- package/src/db/memory.ts +396 -280
- package/src/db/record-id.ts +32 -10
- package/src/db/schema-fingerprint.ts +30 -12
- package/src/db/service-normalization.ts +288 -0
- package/src/db/service.ts +912 -779
- package/src/db/startup.ts +153 -68
- package/src/db/transaction-conflict.ts +15 -0
- package/src/effect/awaitable-effect.ts +96 -0
- package/src/effect/errors.ts +121 -0
- package/src/effect/helpers.ts +123 -0
- package/src/effect/index.ts +24 -0
- package/src/effect/layers.ts +238 -0
- package/src/effect/runtime-ref.ts +25 -0
- package/src/effect/runtime.ts +46 -0
- package/src/effect/services.ts +61 -0
- package/src/effect/zod.ts +43 -0
- package/src/embeddings/provider.ts +128 -83
- package/src/index.ts +48 -1
- package/src/openrouter/direct-provider.ts +11 -35
- package/src/queues/autonomous-job.queue.ts +117 -73
- package/src/queues/context-compaction.queue.ts +50 -17
- package/src/queues/delayed-node-promotion.queue.ts +46 -17
- package/src/queues/document-processor.queue.ts +52 -77
- package/src/queues/memory-consolidation.queue.ts +47 -32
- package/src/queues/organization-learning.queue.ts +26 -4
- package/src/queues/plan-agent-heartbeat.queue.ts +71 -24
- package/src/queues/plan-scheduler.queue.ts +97 -33
- package/src/queues/post-chat-memory.queue.ts +56 -26
- package/src/queues/queue-factory.ts +227 -59
- package/src/queues/standalone-worker.ts +39 -0
- package/src/queues/title-generation.queue.ts +45 -11
- package/src/redis/connection.ts +182 -113
- package/src/redis/index.ts +6 -8
- package/src/redis/org-memory-lock.ts +60 -27
- package/src/redis/redis-lease-lock.ts +200 -121
- package/src/redis/runtime-connection.ts +20 -0
- package/src/redis/stream-context.ts +92 -46
- package/src/runtime/agent-identity-overrides.ts +2 -2
- package/src/runtime/agent-runtime-policy.ts +5 -2
- package/src/runtime/agent-stream-helpers.ts +24 -9
- package/src/runtime/chat-run-orchestration.ts +102 -19
- package/src/runtime/chat-run-registry.ts +36 -2
- package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
- package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +161 -94
- package/src/runtime/domain-layer.ts +192 -0
- package/src/runtime/execution-plan-visibility.ts +2 -2
- package/src/runtime/execution-plan.ts +42 -15
- package/src/runtime/graph-designer.ts +16 -4
- package/src/runtime/helper-model.ts +139 -48
- package/src/runtime/index.ts +7 -8
- package/src/runtime/indexed-repositories-policy.ts +3 -3
- package/src/runtime/{memory-block.ts → memory/memory-block.ts} +50 -36
- package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
- package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +54 -67
- package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
- package/src/runtime/memory/memory-scope.ts +53 -0
- package/src/runtime/plugin-resolution.ts +124 -25
- package/src/runtime/plugin-types.ts +9 -1
- package/src/runtime/post-turn-side-effects.ts +177 -130
- package/src/runtime/retrieval-adapters.ts +40 -6
- package/src/runtime/runtime-accessors.ts +92 -0
- package/src/runtime/runtime-config.ts +150 -61
- package/src/runtime/runtime-extensions.ts +23 -25
- package/src/runtime/runtime-lifecycle.ts +124 -0
- package/src/runtime/runtime-services.ts +386 -0
- package/src/runtime/runtime-token.ts +47 -0
- package/src/runtime/social-chat/social-chat-agent-runner.ts +159 -0
- package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +51 -20
- package/src/runtime/social-chat/social-chat.ts +630 -0
- package/src/runtime/specialist-runner.ts +36 -10
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +433 -0
- package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
- package/src/runtime/thread-chat-helpers.ts +2 -2
- package/src/runtime/thread-plan-turn.ts +2 -1
- package/src/runtime/thread-turn-context.ts +183 -111
- package/src/runtime/turn-lifecycle.ts +93 -27
- package/src/services/agent-activity.service.ts +287 -203
- package/src/services/agent-executor.service.ts +253 -149
- package/src/services/artifact.service.ts +231 -149
- package/src/services/attachment.service.ts +171 -115
- package/src/services/autonomous-job.service.ts +890 -491
- package/src/services/background-work.service.ts +54 -0
- package/src/services/chat-run-registry.service.ts +13 -1
- package/src/services/context-compaction.service.ts +136 -86
- package/src/services/document-chunk.service.ts +151 -88
- package/src/services/execution-plan/execution-plan-approval.ts +26 -0
- package/src/services/execution-plan/execution-plan-context.ts +29 -0
- package/src/services/execution-plan/execution-plan-graph.ts +278 -0
- package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
- package/src/services/execution-plan/execution-plan-spec.ts +75 -0
- package/src/services/execution-plan/execution-plan.service.ts +1041 -0
- package/src/services/feedback-loop.service.ts +132 -76
- package/src/services/global-orchestrator.service.ts +101 -168
- package/src/services/graph-full-routing.ts +193 -0
- package/src/services/index.ts +19 -21
- package/src/services/institutional-memory.service.ts +213 -125
- package/src/services/learned-skill.service.ts +368 -260
- package/src/services/memory/memory-conversation.ts +95 -0
- package/src/services/memory/memory-errors.ts +27 -0
- package/src/services/memory/memory-org-memory.ts +50 -0
- package/src/services/memory/memory-preseeded.ts +86 -0
- package/src/services/memory/memory-rerank.ts +297 -0
- package/src/services/{memory-utils.ts → memory/memory-utils.ts} +6 -5
- package/src/services/memory/memory.service.ts +674 -0
- package/src/services/memory/rerank.service.ts +201 -0
- package/src/services/monitoring-window.service.ts +92 -70
- package/src/services/mutating-approval.service.ts +62 -53
- package/src/services/node-workspace.service.ts +141 -98
- package/src/services/notification.service.ts +29 -16
- package/src/services/organization-member.service.ts +120 -66
- package/src/services/organization.service.ts +153 -77
- package/src/services/ownership-dispatcher.service.ts +456 -263
- package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
- package/src/services/plan/plan-agent-query.service.ts +322 -0
- package/src/services/{plan-approval.service.ts → plan/plan-approval.service.ts} +45 -22
- package/src/services/plan/plan-artifact.service.ts +60 -0
- package/src/services/plan/plan-builder.service.ts +76 -0
- package/src/services/plan/plan-checkpoint.service.ts +103 -0
- package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
- package/src/services/plan/plan-completion-side-effects.ts +169 -0
- package/src/services/plan/plan-coordination.service.ts +181 -0
- package/src/services/plan/plan-cycle.service.ts +405 -0
- package/src/services/plan/plan-deadline.service.ts +533 -0
- package/src/services/plan/plan-event-delivery.service.ts +266 -0
- package/src/services/plan/plan-executor-context.ts +35 -0
- package/src/services/plan/plan-executor-graph.ts +522 -0
- package/src/services/plan/plan-executor-helpers.ts +307 -0
- package/src/services/plan/plan-executor-persistence.ts +209 -0
- package/src/services/plan/plan-executor.service.ts +1737 -0
- package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
- package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
- package/src/services/plan/plan-run-serialization.ts +15 -0
- package/src/services/plan/plan-run.service.ts +637 -0
- package/src/services/plan/plan-scheduler.service.ts +379 -0
- package/src/services/plan/plan-template.service.ts +224 -0
- package/src/services/plan/plan-transaction-events.ts +36 -0
- package/src/services/plan/plan-validator.service.ts +907 -0
- package/src/services/plan/plan-workspace.service.ts +131 -0
- package/src/services/plugin-executor.service.ts +102 -68
- package/src/services/quality-metrics.service.ts +112 -94
- package/src/services/queue-job.service.ts +288 -231
- package/src/services/recent-activity-title.service.ts +73 -36
- package/src/services/recent-activity.service.ts +274 -259
- package/src/services/skill-resolver.service.ts +38 -12
- package/src/services/social-chat-history.service.ts +190 -122
- package/src/services/system-executor.service.ts +96 -61
- package/src/services/thread/thread-active-run.ts +203 -0
- package/src/services/thread/thread-bootstrap.ts +385 -0
- package/src/services/thread/thread-listing.ts +199 -0
- package/src/services/thread/thread-memory-block.ts +130 -0
- package/src/services/thread/thread-message.service.ts +379 -0
- package/src/services/thread/thread-record-store.ts +155 -0
- package/src/services/thread/thread-title.service.ts +74 -0
- package/src/services/thread/thread-turn-execution.ts +280 -0
- package/src/services/thread/thread-turn-message-context.ts +73 -0
- package/src/services/thread/thread-turn-preparation.service.ts +1148 -0
- package/src/services/thread/thread-turn-streaming.ts +403 -0
- package/src/services/thread/thread-turn-tracing.ts +35 -0
- package/src/services/thread/thread-turn.ts +376 -0
- package/src/services/thread/thread.service.ts +344 -0
- package/src/services/user.service.ts +82 -32
- package/src/services/write-intent-validator.service.ts +63 -51
- package/src/storage/attachment-parser.ts +69 -27
- package/src/storage/attachment-storage.service.ts +334 -275
- package/src/storage/generated-document-storage.service.ts +66 -34
- package/src/system-agents/agent-result.ts +3 -1
- package/src/system-agents/context-compaction.agent.ts +3 -3
- package/src/system-agents/delegated-agent-factory.ts +159 -90
- package/src/system-agents/helper-agent-options.ts +1 -1
- package/src/system-agents/memory-reranker.agent.ts +3 -3
- package/src/system-agents/memory.agent.ts +3 -3
- package/src/system-agents/recent-activity-title-refiner.agent.ts +3 -3
- package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -3
- package/src/system-agents/skill-extractor.agent.ts +3 -3
- package/src/system-agents/skill-manager.agent.ts +3 -3
- package/src/system-agents/thread-router.agent.ts +157 -113
- package/src/system-agents/title-generator.agent.ts +3 -3
- package/src/tools/execution-plan.tool.ts +241 -171
- package/src/tools/fetch-webpage.tool.ts +29 -18
- package/src/tools/firecrawl-client.ts +26 -6
- package/src/tools/index.ts +1 -0
- package/src/tools/memory-block.tool.ts +14 -6
- package/src/tools/plan-approval.tool.ts +57 -47
- package/src/tools/read-file-parts.tool.ts +44 -33
- package/src/tools/remember-memory.tool.ts +65 -45
- package/src/tools/search-web.tool.ts +33 -22
- package/src/tools/search.tool.ts +41 -29
- package/src/tools/team-think.tool.ts +125 -84
- package/src/tools/user-questions.tool.ts +4 -3
- package/src/tools/web-tool-shared.ts +6 -0
- package/src/utils/async.ts +25 -22
- package/src/utils/crypto.ts +21 -0
- package/src/utils/date-time.ts +40 -1
- package/src/utils/errors.ts +111 -20
- package/src/utils/hono-error-handler.ts +24 -39
- package/src/utils/index.ts +2 -1
- package/src/utils/null-proto-record.ts +41 -0
- package/src/utils/sse-keepalive.ts +124 -21
- package/src/workers/bootstrap.ts +164 -52
- package/src/workers/memory-consolidation.worker.ts +325 -237
- package/src/workers/organization-learning.worker.ts +50 -16
- package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
- package/src/workers/regular-chat-memory-digest.runner.ts +185 -114
- package/src/workers/skill-extraction.runner.ts +176 -93
- package/src/workers/utils/file-section-chunker.ts +8 -10
- package/src/workers/utils/repo-structure-extractor.ts +349 -260
- package/src/workers/utils/repomix-file-sections.ts +2 -2
- package/src/workers/utils/thread-message-query.ts +97 -38
- package/src/workers/worker-utils.ts +74 -31
- package/src/config/debug-logger.ts +0 -47
- package/src/config/search.ts +0 -3
- package/src/redis/connection-accessor.ts +0 -26
- package/src/runtime/agent-types.ts +0 -1
- package/src/runtime/context-compaction-runtime.ts +0 -87
- package/src/runtime/memory-scope.ts +0 -43
- package/src/runtime/social-chat-agent-runner.ts +0 -118
- package/src/runtime/social-chat.ts +0 -516
- package/src/runtime/team-consultation-orchestrator.ts +0 -272
- package/src/services/adaptive-playbook.service.ts +0 -152
- package/src/services/artifact-provenance.service.ts +0 -172
- package/src/services/chat-attachments.service.ts +0 -17
- package/src/services/context-compaction-runtime.singleton.ts +0 -13
- package/src/services/execution-plan.service.ts +0 -1118
- package/src/services/memory.service.ts +0 -914
- package/src/services/plan-agent-heartbeat.service.ts +0 -136
- package/src/services/plan-agent-query.service.ts +0 -267
- package/src/services/plan-artifact.service.ts +0 -50
- package/src/services/plan-builder.service.ts +0 -67
- package/src/services/plan-checkpoint.service.ts +0 -81
- package/src/services/plan-completion-side-effects.ts +0 -80
- package/src/services/plan-coordination.service.ts +0 -157
- package/src/services/plan-cycle.service.ts +0 -284
- package/src/services/plan-deadline.service.ts +0 -430
- package/src/services/plan-event-delivery.service.ts +0 -166
- package/src/services/plan-executor.service.ts +0 -1950
- package/src/services/plan-run.service.ts +0 -515
- package/src/services/plan-scheduler.service.ts +0 -240
- package/src/services/plan-template.service.ts +0 -177
- package/src/services/plan-validator.service.ts +0 -818
- package/src/services/plan-workspace.service.ts +0 -83
- package/src/services/rerank.service.ts +0 -156
- package/src/services/thread-message.service.ts +0 -275
- package/src/services/thread-plan-registry.service.ts +0 -22
- package/src/services/thread-title.service.ts +0 -39
- package/src/services/thread-turn-preparation.service.ts +0 -1147
- package/src/services/thread-turn.ts +0 -172
- package/src/services/thread.service.ts +0 -869
- package/src/utils/env.ts +0 -8
- /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
- /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
- /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
- /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
- /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
- /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
- /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
- /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
|
@@ -1,13 +1,58 @@
|
|
|
1
|
+
import { normalizeMessageBatch } from '@lota-sdk/shared'
|
|
2
|
+
import { Context, Effect, Layer, Schema } from 'effect'
|
|
1
3
|
import { z } from 'zod'
|
|
2
4
|
|
|
3
|
-
import {
|
|
5
|
+
import { makeEffectTryPromiseWithMessage } from '../effect/helpers'
|
|
6
|
+
import { RedisServiceTag, RuntimeConfigServiceTag } from '../effect/services'
|
|
7
|
+
import type { RedisConnectionManager } from '../redis/connection'
|
|
8
|
+
import type { ResolvedLotaRuntimeConfig } from '../runtime/runtime-config'
|
|
4
9
|
import type { LotaRuntimeBackgroundCursor, LotaRuntimeBackgroundCursorKind } from '../runtime/runtime-extensions'
|
|
5
10
|
|
|
11
|
+
export class SocialChatHistoryError extends Schema.TaggedErrorClass<SocialChatHistoryError>()(
|
|
12
|
+
'SocialChatHistoryError',
|
|
13
|
+
{ message: Schema.String, cause: Schema.optional(Schema.Defect) },
|
|
14
|
+
) {}
|
|
15
|
+
|
|
16
|
+
const tryRedisPromise = makeEffectTryPromiseWithMessage(
|
|
17
|
+
(message, cause) => new SocialChatHistoryError({ message, cause }),
|
|
18
|
+
)
|
|
19
|
+
|
|
6
20
|
const DEFAULT_SOCIAL_CHAT_HISTORY_PREFIX = 'lota:social:history'
|
|
7
21
|
|
|
8
22
|
const SocialChatMessageRoleSchema = z.enum(['user', 'assistant'])
|
|
9
23
|
const SocialChatSourceSchema = z.literal('social')
|
|
10
24
|
const SocialChatPlatformSchema = z.literal('slack')
|
|
25
|
+
const SocialChatHistoryGenericPartSchema = z
|
|
26
|
+
.object({ type: z.string().optional(), text: z.string().optional() })
|
|
27
|
+
.catchall(z.unknown())
|
|
28
|
+
const SocialChatHistoryTextPartSchema = SocialChatHistoryGenericPartSchema.extend({
|
|
29
|
+
type: z.literal('text'),
|
|
30
|
+
text: z.string().trim().min(1),
|
|
31
|
+
})
|
|
32
|
+
const SocialChatHistoryFilePartSchema = SocialChatHistoryGenericPartSchema.extend({
|
|
33
|
+
type: z.literal('file'),
|
|
34
|
+
filename: z.string().trim().min(1),
|
|
35
|
+
mediaType: z.string().trim().min(1),
|
|
36
|
+
sizeBytes: z.number().int().nonnegative().nullable(),
|
|
37
|
+
storageKey: z.string().trim().min(1),
|
|
38
|
+
})
|
|
39
|
+
const SocialChatHistoryPartSchema = z.union([
|
|
40
|
+
SocialChatHistoryTextPartSchema,
|
|
41
|
+
SocialChatHistoryFilePartSchema,
|
|
42
|
+
SocialChatHistoryGenericPartSchema,
|
|
43
|
+
])
|
|
44
|
+
const SocialChatHistoryMetadataSchema = z
|
|
45
|
+
.object({
|
|
46
|
+
platform: SocialChatPlatformSchema,
|
|
47
|
+
channelId: z.string().trim().min(1),
|
|
48
|
+
threadId: z.string().trim().min(1),
|
|
49
|
+
messageId: z.string().trim().min(1),
|
|
50
|
+
authorId: z.string().trim().min(1).optional(),
|
|
51
|
+
authorName: z.string().trim().min(1).optional(),
|
|
52
|
+
agentId: z.string().trim().min(1).optional(),
|
|
53
|
+
agentName: z.string().trim().min(1).optional(),
|
|
54
|
+
})
|
|
55
|
+
.catchall(z.unknown())
|
|
11
56
|
|
|
12
57
|
const SocialChatHistoryMessageSchema = z.object({
|
|
13
58
|
source: SocialChatSourceSchema,
|
|
@@ -18,15 +63,17 @@ const SocialChatHistoryMessageSchema = z.object({
|
|
|
18
63
|
threadId: z.string().trim().min(1),
|
|
19
64
|
messageId: z.string().trim().min(1),
|
|
20
65
|
role: SocialChatMessageRoleSchema,
|
|
21
|
-
parts: z.array(
|
|
22
|
-
metadata:
|
|
66
|
+
parts: z.array(SocialChatHistoryPartSchema),
|
|
67
|
+
metadata: SocialChatHistoryMetadataSchema.optional(),
|
|
23
68
|
cursor: z.object({ createdAt: z.coerce.date(), id: z.string().trim().min(1) }),
|
|
24
69
|
})
|
|
70
|
+
const SocialChatHistoryCursorSchema = z.object({ createdAt: z.coerce.date(), id: z.string().trim().min(1) })
|
|
71
|
+
const decodeJsonStringEffect = Schema.decodeUnknownEffect(Schema.UnknownFromJsonString)
|
|
25
72
|
|
|
73
|
+
export type SocialChatHistoryPart = z.infer<typeof SocialChatHistoryPartSchema>
|
|
74
|
+
export type SocialChatHistoryMetadata = z.infer<typeof SocialChatHistoryMetadataSchema>
|
|
26
75
|
export type SocialChatHistoryMessage = z.infer<typeof SocialChatHistoryMessageSchema>
|
|
27
76
|
|
|
28
|
-
let socialChatHistoryPrefix = DEFAULT_SOCIAL_CHAT_HISTORY_PREFIX
|
|
29
|
-
|
|
30
77
|
function trimConfiguredPrefix(value: string | undefined): string {
|
|
31
78
|
const normalized = value?.trim()
|
|
32
79
|
return normalized && normalized.length > 0 ? normalized : DEFAULT_SOCIAL_CHAT_HISTORY_PREFIX
|
|
@@ -38,160 +85,181 @@ function compareCursorOrder(left: LotaRuntimeBackgroundCursor, right: LotaRuntim
|
|
|
38
85
|
return left.id.localeCompare(right.id)
|
|
39
86
|
}
|
|
40
87
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
88
|
+
export function makeSocialChatHistoryService(
|
|
89
|
+
redis: RedisConnectionManager,
|
|
90
|
+
config: ResolvedLotaRuntimeConfig | undefined,
|
|
91
|
+
) {
|
|
92
|
+
const resolvePrefix = (): string => trimConfiguredPrefix(config?.socialChat?.historyRedisKeyPrefix)
|
|
45
93
|
|
|
46
|
-
|
|
94
|
+
const messageStorageKey = (message: {
|
|
47
95
|
platform: 'slack'
|
|
48
96
|
workspaceId: string
|
|
49
97
|
threadId: string
|
|
50
98
|
messageId: string
|
|
51
|
-
}): string
|
|
52
|
-
|
|
53
|
-
}
|
|
99
|
+
}): string =>
|
|
100
|
+
`${resolvePrefix()}:message:${message.platform}:${message.workspaceId}:${message.threadId}:${message.messageId}`
|
|
54
101
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
102
|
+
const threadIndexKey = (workspaceId: string, threadId: string): string =>
|
|
103
|
+
`${resolvePrefix()}:thread:${workspaceId}:${threadId}`
|
|
58
104
|
|
|
59
|
-
|
|
60
|
-
return `${socialChatHistoryPrefix}:workspace:${workspaceId}`
|
|
61
|
-
}
|
|
105
|
+
const workspaceIndexKey = (workspaceId: string): string => `${resolvePrefix()}:workspace:${workspaceId}`
|
|
62
106
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
107
|
+
const cursorKey = (kind: LotaRuntimeBackgroundCursorKind, workspaceId: string): string =>
|
|
108
|
+
`${resolvePrefix()}:cursor:${kind}:${workspaceId}`
|
|
66
109
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
...message,
|
|
70
|
-
cursor: { ...message.cursor, createdAt: message.cursor.createdAt.toISOString() },
|
|
71
|
-
})
|
|
72
|
-
}
|
|
110
|
+
const serializeMessage = (message: SocialChatHistoryMessage): string =>
|
|
111
|
+
JSON.stringify({ ...message, cursor: { ...message.cursor, createdAt: message.cursor.createdAt.toISOString() } })
|
|
73
112
|
|
|
74
|
-
|
|
75
|
-
if (!value) return null
|
|
76
|
-
try {
|
|
77
|
-
const parsed = SocialChatHistoryMessageSchema.safeParse(JSON.parse(value))
|
|
78
|
-
return parsed.success ? parsed.data : null
|
|
79
|
-
} catch {
|
|
80
|
-
return null
|
|
81
|
-
}
|
|
82
|
-
}
|
|
113
|
+
const parseStoredMessageEffect = (value: string | null): Effect.Effect<SocialChatHistoryMessage | null> => {
|
|
114
|
+
if (!value) return Effect.succeed(null)
|
|
83
115
|
|
|
84
|
-
|
|
85
|
-
|
|
116
|
+
return decodeJsonStringEffect(value).pipe(
|
|
117
|
+
Effect.map((parsed) => {
|
|
118
|
+
const validated = SocialChatHistoryMessageSchema.safeParse(parsed)
|
|
119
|
+
return validated.success ? validated.data : null
|
|
120
|
+
}),
|
|
121
|
+
Effect.catch(() => Effect.succeed(null)),
|
|
122
|
+
)
|
|
86
123
|
}
|
|
87
124
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
125
|
+
const serializeCursor = (cursor: LotaRuntimeBackgroundCursor): string =>
|
|
126
|
+
JSON.stringify({ ...cursor, createdAt: cursor.createdAt.toISOString() })
|
|
127
|
+
|
|
128
|
+
const parseCursorEffect = (value: string | null): Effect.Effect<LotaRuntimeBackgroundCursor | null> => {
|
|
129
|
+
if (!value) return Effect.succeed(null)
|
|
130
|
+
|
|
131
|
+
return decodeJsonStringEffect(value).pipe(
|
|
132
|
+
Effect.map((parsed) => {
|
|
133
|
+
const validated = SocialChatHistoryCursorSchema.safeParse(parsed)
|
|
134
|
+
return validated.success ? validated.data : null
|
|
135
|
+
}),
|
|
136
|
+
Effect.catch(() => Effect.succeed(null)),
|
|
137
|
+
)
|
|
96
138
|
}
|
|
97
139
|
|
|
98
|
-
|
|
99
|
-
|
|
140
|
+
const upsertMessagesEffect = (messages: SocialChatHistoryMessage[]) =>
|
|
141
|
+
Effect.gen(function* () {
|
|
142
|
+
if (messages.length === 0) return [] as SocialChatHistoryMessage[]
|
|
100
143
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
144
|
+
const conn = redis.getConnection()
|
|
145
|
+
const normalizedMessages = normalizeMessageBatch(messages, {
|
|
146
|
+
parseMessage: (message) => SocialChatHistoryMessageSchema.parse(message),
|
|
147
|
+
compareMessages: (left, right) => compareCursorOrder(left.cursor, right.cursor),
|
|
148
|
+
getMessageId: (message) => message.messageId,
|
|
149
|
+
})
|
|
150
|
+
const multi = conn.multi()
|
|
104
151
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
152
|
+
for (const message of normalizedMessages) {
|
|
153
|
+
const storageKey = messageStorageKey(message)
|
|
154
|
+
const score = message.cursor.createdAt.getTime()
|
|
155
|
+
multi.set(storageKey, serializeMessage(message))
|
|
156
|
+
multi.zadd(threadIndexKey(message.workspaceId, message.threadId), score, storageKey)
|
|
157
|
+
multi.zadd(workspaceIndexKey(message.workspaceId), score, storageKey)
|
|
158
|
+
}
|
|
112
159
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
160
|
+
yield* tryRedisPromise(() => multi.exec(), 'Failed to upsert social chat messages.')
|
|
161
|
+
return normalizedMessages
|
|
162
|
+
})
|
|
116
163
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
164
|
+
const listThreadMessagesEffect = (params: { workspaceId: string; threadId: string }) =>
|
|
165
|
+
Effect.gen(function* () {
|
|
166
|
+
const conn = redis.getConnection()
|
|
167
|
+
const storageKeys = yield* tryRedisPromise(
|
|
168
|
+
() => conn.zrange(threadIndexKey(params.workspaceId, params.threadId), 0, -1),
|
|
169
|
+
'Failed to list thread message keys.',
|
|
170
|
+
)
|
|
171
|
+
if (storageKeys.length === 0) return [] as SocialChatHistoryMessage[]
|
|
121
172
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
173
|
+
const storedValues = yield* tryRedisPromise(() => conn.mget(storageKeys), 'Failed to read thread messages.')
|
|
174
|
+
const parsedMessages = yield* Effect.forEach(storedValues, parseStoredMessageEffect)
|
|
175
|
+
return parsedMessages
|
|
176
|
+
.filter((message): message is SocialChatHistoryMessage => message !== null)
|
|
177
|
+
.sort((left, right) => compareCursorOrder(left.cursor, right.cursor))
|
|
178
|
+
})
|
|
128
179
|
|
|
129
|
-
|
|
180
|
+
const listWorkspaceMessagesEffect = (params: {
|
|
130
181
|
workspaceId: string
|
|
131
182
|
cursor: LotaRuntimeBackgroundCursor | null
|
|
132
183
|
onboardingCutoff: Date | null
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
184
|
+
}) =>
|
|
185
|
+
Effect.gen(function* () {
|
|
186
|
+
const conn = redis.getConnection()
|
|
187
|
+
const indexKey = workspaceIndexKey(params.workspaceId)
|
|
188
|
+
const scoreStart =
|
|
189
|
+
params.cursor?.createdAt.getTime() ??
|
|
190
|
+
(params.onboardingCutoff ? params.onboardingCutoff.getTime() : Number.NEGATIVE_INFINITY)
|
|
191
|
+
const storageKeys = yield* tryRedisPromise(
|
|
192
|
+
() =>
|
|
193
|
+
params.cursor || params.onboardingCutoff
|
|
194
|
+
? conn.zrangebyscore(indexKey, scoreStart, '+inf')
|
|
195
|
+
: conn.zrange(indexKey, 0, -1),
|
|
196
|
+
'Failed to list workspace message keys.',
|
|
197
|
+
)
|
|
198
|
+
if (storageKeys.length === 0) return [] as SocialChatHistoryMessage[]
|
|
199
|
+
|
|
200
|
+
const storedValues = yield* tryRedisPromise(() => conn.mget(storageKeys), 'Failed to read workspace messages.')
|
|
201
|
+
const parsedMessages = yield* Effect.forEach(storedValues, parseStoredMessageEffect)
|
|
202
|
+
return parsedMessages
|
|
203
|
+
.filter((message): message is SocialChatHistoryMessage => message !== null)
|
|
204
|
+
.filter((message) => {
|
|
205
|
+
if (params.cursor) {
|
|
206
|
+
return compareCursorOrder(message.cursor, params.cursor) > 0
|
|
207
|
+
}
|
|
208
|
+
if (params.onboardingCutoff) {
|
|
209
|
+
return message.cursor.createdAt.getTime() > params.onboardingCutoff.getTime()
|
|
210
|
+
}
|
|
211
|
+
return true
|
|
212
|
+
})
|
|
213
|
+
.sort((left, right) => compareCursorOrder(left.cursor, right.cursor))
|
|
214
|
+
})
|
|
161
215
|
|
|
162
|
-
|
|
216
|
+
const hasWorkspaceMessagesEffect = (params: {
|
|
163
217
|
workspaceId: string
|
|
164
218
|
cursor: LotaRuntimeBackgroundCursor | null
|
|
165
219
|
onboardingCutoff: Date | null
|
|
166
|
-
})
|
|
167
|
-
|
|
220
|
+
}) =>
|
|
221
|
+
listWorkspaceMessagesEffect({
|
|
168
222
|
workspaceId: params.workspaceId,
|
|
169
223
|
cursor: params.cursor,
|
|
170
224
|
onboardingCutoff: params.onboardingCutoff,
|
|
171
|
-
})
|
|
172
|
-
return messages.length > 0
|
|
173
|
-
}
|
|
225
|
+
}).pipe(Effect.map((messages) => messages.length > 0))
|
|
174
226
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return this.parseCursor(await redis.get(this.cursorKey(kind, workspaceId)))
|
|
181
|
-
}
|
|
227
|
+
const getBackgroundCursorEffect = (kind: LotaRuntimeBackgroundCursorKind, workspaceId: string) =>
|
|
228
|
+
tryRedisPromise(
|
|
229
|
+
() => redis.getConnection().get(cursorKey(kind, workspaceId)),
|
|
230
|
+
'Failed to read background cursor.',
|
|
231
|
+
).pipe(Effect.flatMap((value) => parseCursorEffect(value)))
|
|
182
232
|
|
|
183
|
-
|
|
233
|
+
const setBackgroundCursorEffect = (
|
|
184
234
|
kind: LotaRuntimeBackgroundCursorKind,
|
|
185
235
|
workspaceId: string,
|
|
186
236
|
cursor: LotaRuntimeBackgroundCursor,
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
|
|
237
|
+
) =>
|
|
238
|
+
tryRedisPromise(
|
|
239
|
+
() => redis.getConnection().set(cursorKey(kind, workspaceId), serializeCursor(cursor)),
|
|
240
|
+
'Failed to write background cursor.',
|
|
241
|
+
).pipe(Effect.asVoid)
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
upsertMessages: upsertMessagesEffect,
|
|
245
|
+
listThreadMessages: listThreadMessagesEffect,
|
|
246
|
+
listWorkspaceMessages: listWorkspaceMessagesEffect,
|
|
247
|
+
hasWorkspaceMessages: hasWorkspaceMessagesEffect,
|
|
248
|
+
getBackgroundCursor: getBackgroundCursorEffect,
|
|
249
|
+
setBackgroundCursor: setBackgroundCursorEffect,
|
|
190
250
|
}
|
|
191
251
|
}
|
|
192
252
|
|
|
193
|
-
export
|
|
253
|
+
export class SocialChatHistoryServiceTag extends Context.Service<
|
|
254
|
+
SocialChatHistoryServiceTag,
|
|
255
|
+
ReturnType<typeof makeSocialChatHistoryService>
|
|
256
|
+
>()('@lota-sdk/core/SocialChatHistoryService') {}
|
|
194
257
|
|
|
195
|
-
export
|
|
196
|
-
|
|
197
|
-
|
|
258
|
+
export const SocialChatHistoryServiceLive = Layer.effect(
|
|
259
|
+
SocialChatHistoryServiceTag,
|
|
260
|
+
Effect.gen(function* () {
|
|
261
|
+
const redis = yield* RedisServiceTag
|
|
262
|
+
const config = yield* RuntimeConfigServiceTag
|
|
263
|
+
return makeSocialChatHistoryService(redis, config)
|
|
264
|
+
}),
|
|
265
|
+
)
|
|
@@ -1,27 +1,38 @@
|
|
|
1
1
|
import type { OwnershipDispatchContext, PlanNodeResult, PlanNodeSpec, SystemPlanNodeOwner } from '@lota-sdk/shared'
|
|
2
|
+
import { Context, Effect, Layer } from 'effect'
|
|
2
3
|
|
|
4
|
+
import { BadRequestError, ServiceError } from '../effect/errors'
|
|
5
|
+
import { makeEffectTryPromiseWithMessage } from '../effect/helpers'
|
|
6
|
+
import { RuntimeConfigServiceTag } from '../effect/services'
|
|
7
|
+
|
|
8
|
+
const trySystemExecutorPromise = makeEffectTryPromiseWithMessage(
|
|
9
|
+
(message, cause) => new ServiceError({ message, cause }),
|
|
10
|
+
)
|
|
3
11
|
import type { SystemNodeExecutor, PluginNodeExecutionParams } from '../runtime/plugin-types'
|
|
4
|
-
import {
|
|
5
|
-
import type { PlanValidationIssueInput } from './plan-validator.service'
|
|
12
|
+
import type { ResolvedLotaRuntimeConfig } from '../runtime/runtime-config'
|
|
13
|
+
import type { PlanValidationIssueInput } from './plan/plan-validator.service'
|
|
6
14
|
|
|
7
15
|
const BUILT_IN_SYSTEM_EXECUTORS = Object.freeze({
|
|
8
16
|
'plan-runtime': {
|
|
9
17
|
supportedOperations: ['echo-input'],
|
|
10
|
-
|
|
11
|
-
return
|
|
18
|
+
executeNode(params: PluginNodeExecutionParams): Promise<PlanNodeResult> {
|
|
19
|
+
return Promise.resolve({
|
|
20
|
+
notes: 'System echo-input completed.',
|
|
21
|
+
structuredOutput: structuredClone(params.inputs),
|
|
22
|
+
artifacts: [],
|
|
23
|
+
})
|
|
12
24
|
},
|
|
13
25
|
} satisfies SystemNodeExecutor,
|
|
14
26
|
})
|
|
15
27
|
|
|
16
|
-
|
|
17
|
-
|
|
28
|
+
function isSystemOwner(
|
|
29
|
+
owner: PlanNodeSpec['owner'],
|
|
30
|
+
): owner is Extract<PlanNodeSpec['owner'], { executorType: 'system' }> {
|
|
31
|
+
return owner.executorType === 'system'
|
|
18
32
|
}
|
|
19
33
|
|
|
20
|
-
function
|
|
21
|
-
return (
|
|
22
|
-
string,
|
|
23
|
-
SystemNodeExecutor | undefined
|
|
24
|
-
>
|
|
34
|
+
export function getBuiltInSystemExecutors(): Record<string, SystemNodeExecutor> {
|
|
35
|
+
return Object.fromEntries(Object.entries(BUILT_IN_SYSTEM_EXECUTORS))
|
|
25
36
|
}
|
|
26
37
|
|
|
27
38
|
function buildSystemExecutionParams(params: {
|
|
@@ -45,61 +56,85 @@ function buildSystemExecutionParams(params: {
|
|
|
45
56
|
}
|
|
46
57
|
}
|
|
47
58
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return [
|
|
53
|
-
{
|
|
54
|
-
severity: 'blocking',
|
|
55
|
-
code: 'system_executor_missing',
|
|
56
|
-
message: `Node "${nodeId}" references unknown system executor "${owner.ref}".`,
|
|
57
|
-
nodeId,
|
|
58
|
-
detail: { systemRef: owner.ref },
|
|
59
|
-
},
|
|
60
|
-
]
|
|
61
|
-
}
|
|
59
|
+
export function makeSystemExecutorService(config: ResolvedLotaRuntimeConfig) {
|
|
60
|
+
function getSystemExecutors() {
|
|
61
|
+
return (config.systemExecutors ?? getBuiltInSystemExecutors()) as Record<string, SystemNodeExecutor | undefined>
|
|
62
|
+
}
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
64
|
+
return {
|
|
65
|
+
validateOwner(owner: SystemPlanNodeOwner, nodeId: string): PlanValidationIssueInput[] {
|
|
66
|
+
const executor = getSystemExecutors()[owner.ref]
|
|
67
|
+
if (!executor) {
|
|
68
|
+
return [
|
|
69
|
+
{
|
|
70
|
+
severity: 'blocking',
|
|
71
|
+
code: 'system_executor_missing',
|
|
72
|
+
message: `Node "${nodeId}" references unknown system executor "${owner.ref}".`,
|
|
73
|
+
nodeId,
|
|
74
|
+
detail: { systemRef: owner.ref },
|
|
75
|
+
},
|
|
76
|
+
]
|
|
77
|
+
}
|
|
74
78
|
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
if (!executor.supportedOperations.includes(owner.operation)) {
|
|
80
|
+
return [
|
|
81
|
+
{
|
|
82
|
+
severity: 'blocking',
|
|
83
|
+
code: 'system_operation_missing',
|
|
84
|
+
message: `System executor "${owner.ref}" does not support operation "${owner.operation}".`,
|
|
85
|
+
nodeId,
|
|
86
|
+
detail: { systemRef: owner.ref, operation: owner.operation },
|
|
87
|
+
},
|
|
88
|
+
]
|
|
89
|
+
}
|
|
77
90
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
resolvedInput: Record<string, unknown>
|
|
81
|
-
context: OwnershipDispatchContext
|
|
82
|
-
}): Promise<PlanNodeResult> {
|
|
83
|
-
if (params.nodeSpec.owner.executorType !== 'system') {
|
|
84
|
-
throw new Error(`SystemExecutor cannot execute owner type "${params.nodeSpec.owner.executorType}".`)
|
|
85
|
-
}
|
|
91
|
+
return []
|
|
92
|
+
},
|
|
86
93
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
executeNode: Effect.fn('SystemExecutor.executeNode')(function* (params: {
|
|
95
|
+
nodeSpec: PlanNodeSpec
|
|
96
|
+
resolvedInput: Record<string, unknown>
|
|
97
|
+
context: OwnershipDispatchContext
|
|
98
|
+
}) {
|
|
99
|
+
const owner = params.nodeSpec.owner
|
|
100
|
+
if (!isSystemOwner(owner)) {
|
|
101
|
+
return yield* new BadRequestError({
|
|
102
|
+
message: `SystemExecutor cannot execute owner type "${owner.executorType}".`,
|
|
103
|
+
})
|
|
104
|
+
}
|
|
93
105
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
106
|
+
const executor = getSystemExecutors()[owner.ref]
|
|
107
|
+
if (!executor || !executor.supportedOperations.includes(owner.operation)) {
|
|
108
|
+
return yield* new BadRequestError({
|
|
109
|
+
message: `System executor ${owner.ref}.${owner.operation} is not registered.`,
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return yield* trySystemExecutorPromise(
|
|
114
|
+
() =>
|
|
115
|
+
executor.executeNode(
|
|
116
|
+
buildSystemExecutionParams({
|
|
117
|
+
owner,
|
|
118
|
+
nodeSpec: params.nodeSpec,
|
|
119
|
+
resolvedInput: params.resolvedInput,
|
|
120
|
+
context: params.context,
|
|
121
|
+
}),
|
|
122
|
+
),
|
|
123
|
+
`System executor "${owner.ref}.${owner.operation}" failed.`,
|
|
124
|
+
).pipe(Effect.withSpan('SystemExecutor.invoke'))
|
|
125
|
+
}),
|
|
102
126
|
}
|
|
103
127
|
}
|
|
104
128
|
|
|
105
|
-
export
|
|
129
|
+
export class SystemExecutorServiceTag extends Context.Service<
|
|
130
|
+
SystemExecutorServiceTag,
|
|
131
|
+
ReturnType<typeof makeSystemExecutorService>
|
|
132
|
+
>()('@lota-sdk/core/SystemExecutorService') {}
|
|
133
|
+
|
|
134
|
+
export const SystemExecutorServiceLive = Layer.effect(
|
|
135
|
+
SystemExecutorServiceTag,
|
|
136
|
+
Effect.gen(function* () {
|
|
137
|
+
const runtimeConfig = yield* RuntimeConfigServiceTag
|
|
138
|
+
return makeSystemExecutorService(runtimeConfig)
|
|
139
|
+
}),
|
|
140
|
+
)
|