@lota-sdk/core 0.4.7 → 0.4.9
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 +94 -22
- package/src/ai-gateway/ai-gateway.ts +738 -223
- package/src/config/agent-defaults.ts +176 -75
- package/src/config/agent-types.ts +54 -4
- package/src/config/constants.ts +8 -2
- package/src/config/logger.ts +286 -19
- package/src/config/model-constants.ts +1 -0
- package/src/config/thread-defaults.ts +33 -21
- package/src/create-runtime.ts +725 -383
- package/src/db/base.service.ts +52 -28
- package/src/db/cursor-pagination.ts +71 -30
- package/src/db/memory-store.helpers.ts +4 -7
- package/src/db/memory-store.ts +856 -598
- package/src/db/memory.ts +398 -275
- package/src/db/record-id.ts +32 -10
- package/src/db/schema-fingerprint.ts +30 -12
- package/src/db/service-normalization.ts +255 -0
- package/src/db/service.ts +726 -761
- package/src/db/startup.ts +140 -66
- package/src/db/transaction-conflict.ts +15 -0
- package/src/effect/awaitable-effect.ts +87 -0
- package/src/effect/errors.ts +121 -0
- package/src/effect/helpers.ts +98 -0
- package/src/effect/index.ts +22 -0
- package/src/effect/layers.ts +228 -0
- package/src/effect/runtime-ref.ts +25 -0
- package/src/effect/runtime.ts +31 -0
- package/src/effect/services.ts +57 -0
- package/src/effect/zod.ts +43 -0
- package/src/embeddings/provider.ts +122 -71
- package/src/index.ts +46 -1
- package/src/openrouter/direct-provider.ts +29 -0
- package/src/queues/autonomous-job.queue.ts +130 -74
- package/src/queues/context-compaction.queue.ts +60 -15
- package/src/queues/delayed-node-promotion.queue.ts +52 -15
- 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 +13 -4
- package/src/queues/plan-agent-heartbeat.queue.ts +65 -21
- package/src/queues/plan-scheduler.queue.ts +107 -31
- package/src/queues/post-chat-memory.queue.ts +66 -24
- package/src/queues/queue-factory.ts +142 -52
- package/src/queues/standalone-worker.ts +39 -0
- package/src/queues/title-generation.queue.ts +54 -9
- package/src/redis/connection.ts +84 -32
- 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 +10 -0
- package/src/redis/stream-context.ts +84 -46
- package/src/runtime/agent-identity-overrides.ts +2 -2
- package/src/runtime/agent-runtime-policy.ts +4 -1
- package/src/runtime/agent-stream-helpers.ts +20 -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} +114 -91
- package/src/runtime/execution-plan-visibility.ts +2 -2
- package/src/runtime/execution-plan.ts +42 -15
- package/src/runtime/graph-designer.ts +11 -7
- package/src/runtime/helper-model.ts +135 -48
- package/src/runtime/index.ts +7 -7
- package/src/runtime/indexed-repositories-policy.ts +3 -3
- package/src/runtime/{memory-block.ts → memory/memory-block.ts} +40 -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} +1 -1
- package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
- package/src/runtime/{memory-scope.ts → memory/memory-scope.ts} +12 -6
- package/src/runtime/plugin-resolution.ts +144 -24
- package/src/runtime/plugin-types.ts +9 -1
- package/src/runtime/post-turn-side-effects.ts +197 -130
- package/src/runtime/retrieval-adapters.ts +38 -4
- package/src/runtime/runtime-config.ts +165 -61
- package/src/runtime/runtime-extensions.ts +21 -34
- package/src/runtime/social-chat/social-chat-agent-runner.ts +157 -0
- package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +42 -20
- package/src/runtime/social-chat/social-chat.ts +594 -0
- package/src/runtime/specialist-runner.ts +36 -10
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +427 -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 +172 -94
- package/src/runtime/turn-lifecycle.ts +93 -27
- package/src/services/agent-activity.service.ts +287 -203
- package/src/services/agent-executor.service.ts +329 -217
- package/src/services/artifact.service.ts +225 -148
- package/src/services/attachment.service.ts +137 -115
- package/src/services/autonomous-job.service.ts +888 -491
- package/src/services/chat-run-registry.service.ts +11 -1
- package/src/services/context-compaction.service.ts +136 -86
- package/src/services/document-chunk.service.ts +162 -90
- 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 +256 -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 +80 -170
- package/src/services/graph-full-routing.ts +182 -0
- package/src/services/index.ts +18 -20
- package/src/services/institutional-memory.service.ts +220 -123
- package/src/services/learned-skill.service.ts +364 -259
- package/src/services/memory/memory-conversation.ts +95 -0
- package/src/services/memory/memory-org-memory.ts +39 -0
- package/src/services/memory/memory-preseeded.ts +80 -0
- package/src/services/memory/memory-rerank.ts +297 -0
- package/src/services/{memory-utils.ts → memory/memory-utils.ts} +5 -5
- package/src/services/memory/memory.service.ts +692 -0
- package/src/services/memory/rerank.service.ts +209 -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 +17 -16
- package/src/services/organization-member.service.ts +120 -66
- package/src/services/organization.service.ts +144 -51
- package/src/services/ownership-dispatcher.service.ts +415 -264
- 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/plan-approval.service.ts +102 -0
- 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 +175 -0
- package/src/services/plan/plan-coordination.service.ts +181 -0
- package/src/services/plan/plan-cycle.service.ts +398 -0
- package/src/services/plan/plan-deadline.service.ts +547 -0
- package/src/services/plan/plan-event-delivery.service.ts +261 -0
- package/src/services/plan/plan-executor-context.ts +35 -0
- package/src/services/plan/plan-executor-graph.ts +475 -0
- package/src/services/plan/plan-executor-helpers.ts +322 -0
- package/src/services/plan/plan-executor-persistence.ts +209 -0
- package/src/services/plan/plan-executor.service.ts +1654 -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 +644 -0
- package/src/services/plan/plan-scheduler.service.ts +385 -0
- package/src/services/plan/plan-template.service.ts +224 -0
- package/src/services/plan/plan-transaction-events.ts +33 -0
- package/src/services/plan/plan-validator.service.ts +907 -0
- package/src/services/plan/plan-workspace.service.ts +125 -0
- package/src/services/plugin-executor.service.ts +97 -68
- package/src/services/quality-metrics.service.ts +112 -94
- package/src/services/queue-job.service.ts +296 -230
- package/src/services/recent-activity-title.service.ts +65 -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 +176 -125
- package/src/services/system-executor.service.ts +91 -61
- package/src/services/thread/thread-active-run.ts +203 -0
- package/src/services/thread/thread-bootstrap.ts +369 -0
- package/src/services/thread/thread-listing.ts +198 -0
- package/src/services/thread/thread-memory-block.ts +117 -0
- package/src/services/thread/thread-message.service.ts +363 -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 +1146 -0
- package/src/services/thread/thread-turn-streaming.ts +402 -0
- package/src/services/thread/thread-turn-tracing.ts +35 -0
- package/src/services/thread/thread-turn.ts +343 -0
- package/src/services/thread/thread.service.ts +335 -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 +331 -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 +2 -2
- package/src/system-agents/delegated-agent-factory.ts +159 -90
- package/src/system-agents/memory-reranker.agent.ts +2 -2
- package/src/system-agents/memory.agent.ts +2 -2
- package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
- package/src/system-agents/regular-chat-memory-digest.agent.ts +2 -2
- package/src/system-agents/skill-extractor.agent.ts +2 -2
- package/src/system-agents/skill-manager.agent.ts +2 -2
- package/src/system-agents/thread-router.agent.ts +157 -113
- package/src/system-agents/title-generator.agent.ts +2 -2
- package/src/tools/execution-plan.tool.ts +220 -161
- package/src/tools/fetch-webpage.tool.ts +21 -17
- package/src/tools/firecrawl-client.ts +16 -6
- package/src/tools/index.ts +1 -0
- package/src/tools/memory-block.tool.ts +14 -6
- package/src/tools/plan-approval.tool.ts +49 -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 +26 -22
- package/src/tools/search.tool.ts +41 -29
- package/src/tools/team-think.tool.ts +124 -83
- package/src/tools/user-questions.tool.ts +4 -3
- package/src/tools/web-tool-shared.ts +6 -0
- package/src/utils/async.ts +17 -23
- package/src/utils/crypto.ts +21 -0
- package/src/utils/date-time.ts +40 -1
- package/src/utils/errors.ts +95 -16
- 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 +186 -51
- 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 +175 -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 +56 -31
- package/src/config/debug-logger.ts +0 -47
- package/src/redis/connection-accessor.ts +0 -26
- package/src/runtime/context-compaction-runtime.ts +0 -87
- 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 -844
- package/src/services/plan-agent-heartbeat.service.ts +0 -136
- package/src/services/plan-agent-query.service.ts +0 -267
- package/src/services/plan-approval.service.ts +0 -83
- 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/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,6 +1,10 @@
|
|
|
1
|
+
import { normalizeMessageBatch } from '@lota-sdk/shared'
|
|
2
|
+
import { Context, Effect, Layer } from 'effect'
|
|
1
3
|
import { z } from 'zod'
|
|
2
4
|
|
|
3
|
-
import {
|
|
5
|
+
import { RedisServiceTag, RuntimeConfigServiceTag } from '../effect/services'
|
|
6
|
+
import type { RedisConnectionManager } from '../redis/connection'
|
|
7
|
+
import type { ResolvedLotaRuntimeConfig } from '../runtime/runtime-config'
|
|
4
8
|
import type { LotaRuntimeBackgroundCursor, LotaRuntimeBackgroundCursorKind } from '../runtime/runtime-extensions'
|
|
5
9
|
|
|
6
10
|
const DEFAULT_SOCIAL_CHAT_HISTORY_PREFIX = 'lota:social:history'
|
|
@@ -8,6 +12,37 @@ const DEFAULT_SOCIAL_CHAT_HISTORY_PREFIX = 'lota:social:history'
|
|
|
8
12
|
const SocialChatMessageRoleSchema = z.enum(['user', 'assistant'])
|
|
9
13
|
const SocialChatSourceSchema = z.literal('social')
|
|
10
14
|
const SocialChatPlatformSchema = z.literal('slack')
|
|
15
|
+
const SocialChatHistoryGenericPartSchema = z
|
|
16
|
+
.object({ type: z.string().optional(), text: z.string().optional() })
|
|
17
|
+
.catchall(z.unknown())
|
|
18
|
+
const SocialChatHistoryTextPartSchema = SocialChatHistoryGenericPartSchema.extend({
|
|
19
|
+
type: z.literal('text'),
|
|
20
|
+
text: z.string().trim().min(1),
|
|
21
|
+
})
|
|
22
|
+
const SocialChatHistoryFilePartSchema = SocialChatHistoryGenericPartSchema.extend({
|
|
23
|
+
type: z.literal('file'),
|
|
24
|
+
filename: z.string().trim().min(1),
|
|
25
|
+
mediaType: z.string().trim().min(1),
|
|
26
|
+
sizeBytes: z.number().int().nonnegative().nullable(),
|
|
27
|
+
storageKey: z.string().trim().min(1),
|
|
28
|
+
})
|
|
29
|
+
const SocialChatHistoryPartSchema = z.union([
|
|
30
|
+
SocialChatHistoryTextPartSchema,
|
|
31
|
+
SocialChatHistoryFilePartSchema,
|
|
32
|
+
SocialChatHistoryGenericPartSchema,
|
|
33
|
+
])
|
|
34
|
+
const SocialChatHistoryMetadataSchema = z
|
|
35
|
+
.object({
|
|
36
|
+
platform: SocialChatPlatformSchema,
|
|
37
|
+
channelId: z.string().trim().min(1),
|
|
38
|
+
threadId: z.string().trim().min(1),
|
|
39
|
+
messageId: z.string().trim().min(1),
|
|
40
|
+
authorId: z.string().trim().min(1).optional(),
|
|
41
|
+
authorName: z.string().trim().min(1).optional(),
|
|
42
|
+
agentId: z.string().trim().min(1).optional(),
|
|
43
|
+
agentName: z.string().trim().min(1).optional(),
|
|
44
|
+
})
|
|
45
|
+
.catchall(z.unknown())
|
|
11
46
|
|
|
12
47
|
const SocialChatHistoryMessageSchema = z.object({
|
|
13
48
|
source: SocialChatSourceSchema,
|
|
@@ -18,15 +53,15 @@ const SocialChatHistoryMessageSchema = z.object({
|
|
|
18
53
|
threadId: z.string().trim().min(1),
|
|
19
54
|
messageId: z.string().trim().min(1),
|
|
20
55
|
role: SocialChatMessageRoleSchema,
|
|
21
|
-
parts: z.array(
|
|
22
|
-
metadata:
|
|
56
|
+
parts: z.array(SocialChatHistoryPartSchema),
|
|
57
|
+
metadata: SocialChatHistoryMetadataSchema.optional(),
|
|
23
58
|
cursor: z.object({ createdAt: z.coerce.date(), id: z.string().trim().min(1) }),
|
|
24
59
|
})
|
|
25
60
|
|
|
61
|
+
export type SocialChatHistoryPart = z.infer<typeof SocialChatHistoryPartSchema>
|
|
62
|
+
export type SocialChatHistoryMetadata = z.infer<typeof SocialChatHistoryMetadataSchema>
|
|
26
63
|
export type SocialChatHistoryMessage = z.infer<typeof SocialChatHistoryMessageSchema>
|
|
27
64
|
|
|
28
|
-
let socialChatHistoryPrefix = DEFAULT_SOCIAL_CHAT_HISTORY_PREFIX
|
|
29
|
-
|
|
30
65
|
function trimConfiguredPrefix(value: string | undefined): string {
|
|
31
66
|
const normalized = value?.trim()
|
|
32
67
|
return normalized && normalized.length > 0 ? normalized : DEFAULT_SOCIAL_CHAT_HISTORY_PREFIX
|
|
@@ -38,160 +73,176 @@ function compareCursorOrder(left: LotaRuntimeBackgroundCursor, right: LotaRuntim
|
|
|
38
73
|
return left.id.localeCompare(right.id)
|
|
39
74
|
}
|
|
40
75
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
76
|
+
export function makeSocialChatHistoryService(
|
|
77
|
+
redis: RedisConnectionManager,
|
|
78
|
+
config: ResolvedLotaRuntimeConfig | undefined,
|
|
79
|
+
) {
|
|
80
|
+
const resolvePrefix = (): string => trimConfiguredPrefix(config?.socialChat?.historyRedisKeyPrefix)
|
|
45
81
|
|
|
46
|
-
|
|
82
|
+
const messageStorageKey = (message: {
|
|
47
83
|
platform: 'slack'
|
|
48
84
|
workspaceId: string
|
|
49
85
|
threadId: string
|
|
50
86
|
messageId: string
|
|
51
|
-
}): string
|
|
52
|
-
|
|
53
|
-
}
|
|
87
|
+
}): string =>
|
|
88
|
+
`${resolvePrefix()}:message:${message.platform}:${message.workspaceId}:${message.threadId}:${message.messageId}`
|
|
54
89
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
90
|
+
const threadIndexKey = (workspaceId: string, threadId: string): string =>
|
|
91
|
+
`${resolvePrefix()}:thread:${workspaceId}:${threadId}`
|
|
58
92
|
|
|
59
|
-
|
|
60
|
-
return `${socialChatHistoryPrefix}:workspace:${workspaceId}`
|
|
61
|
-
}
|
|
93
|
+
const workspaceIndexKey = (workspaceId: string): string => `${resolvePrefix()}:workspace:${workspaceId}`
|
|
62
94
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
95
|
+
const cursorKey = (kind: LotaRuntimeBackgroundCursorKind, workspaceId: string): string =>
|
|
96
|
+
`${resolvePrefix()}:cursor:${kind}:${workspaceId}`
|
|
66
97
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
...message,
|
|
70
|
-
cursor: { ...message.cursor, createdAt: message.cursor.createdAt.toISOString() },
|
|
71
|
-
})
|
|
72
|
-
}
|
|
98
|
+
const serializeMessage = (message: SocialChatHistoryMessage): string =>
|
|
99
|
+
JSON.stringify({ ...message, cursor: { ...message.cursor, createdAt: message.cursor.createdAt.toISOString() } })
|
|
73
100
|
|
|
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
|
-
}
|
|
101
|
+
const parseStoredMessageEffect = (value: string | null): Effect.Effect<SocialChatHistoryMessage | null> => {
|
|
102
|
+
if (!value) return Effect.succeed(null)
|
|
83
103
|
|
|
84
|
-
|
|
85
|
-
|
|
104
|
+
return Effect.try({ try: (): unknown => JSON.parse(value), catch: (cause) => cause }).pipe(
|
|
105
|
+
Effect.map((parsed) => {
|
|
106
|
+
const validated = SocialChatHistoryMessageSchema.safeParse(parsed)
|
|
107
|
+
return validated.success ? validated.data : null
|
|
108
|
+
}),
|
|
109
|
+
Effect.catch(() => Effect.succeed(null)),
|
|
110
|
+
)
|
|
86
111
|
}
|
|
87
112
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
const parsed = z.object({ createdAt: z.coerce.date(), id: z.string().trim().min(1) }).safeParse(JSON.parse(value))
|
|
92
|
-
return parsed.success ? parsed.data : null
|
|
93
|
-
} catch {
|
|
94
|
-
return null
|
|
95
|
-
}
|
|
96
|
-
}
|
|
113
|
+
const serializeCursor = (cursor: LotaRuntimeBackgroundCursor): string =>
|
|
114
|
+
JSON.stringify({ ...cursor, createdAt: cursor.createdAt.toISOString() })
|
|
97
115
|
|
|
98
|
-
|
|
99
|
-
if (
|
|
116
|
+
const parseCursorEffect = (value: string | null): Effect.Effect<LotaRuntimeBackgroundCursor | null> => {
|
|
117
|
+
if (!value) return Effect.succeed(null)
|
|
100
118
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
multi.set(storageKey, this.serializeMessage(message))
|
|
109
|
-
multi.zadd(this.threadIndexKey(message.workspaceId, message.threadId), score, storageKey)
|
|
110
|
-
multi.zadd(this.workspaceIndexKey(message.workspaceId), score, storageKey)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
await multi.exec()
|
|
114
|
-
return normalizedMessages
|
|
119
|
+
return Effect.try({ try: (): unknown => JSON.parse(value), catch: (cause) => cause }).pipe(
|
|
120
|
+
Effect.map((parsed) => {
|
|
121
|
+
const validated = z.object({ createdAt: z.coerce.date(), id: z.string().trim().min(1) }).safeParse(parsed)
|
|
122
|
+
return validated.success ? validated.data : null
|
|
123
|
+
}),
|
|
124
|
+
Effect.catch(() => Effect.succeed(null)),
|
|
125
|
+
)
|
|
115
126
|
}
|
|
116
127
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (storageKeys.length === 0) return []
|
|
128
|
+
const upsertMessagesEffect = (messages: SocialChatHistoryMessage[]) =>
|
|
129
|
+
Effect.gen(function* () {
|
|
130
|
+
if (messages.length === 0) return [] as SocialChatHistoryMessage[]
|
|
121
131
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
const conn = redis.getConnection()
|
|
133
|
+
const normalizedMessages = normalizeMessageBatch(messages, {
|
|
134
|
+
parseMessage: (message) => SocialChatHistoryMessageSchema.parse(message),
|
|
135
|
+
compareMessages: (left, right) => compareCursorOrder(left.cursor, right.cursor),
|
|
136
|
+
getMessageId: (message) => message.messageId,
|
|
137
|
+
})
|
|
138
|
+
const multi = conn.multi()
|
|
139
|
+
|
|
140
|
+
for (const message of normalizedMessages) {
|
|
141
|
+
const storageKey = messageStorageKey(message)
|
|
142
|
+
const score = message.cursor.createdAt.getTime()
|
|
143
|
+
multi.set(storageKey, serializeMessage(message))
|
|
144
|
+
multi.zadd(threadIndexKey(message.workspaceId, message.threadId), score, storageKey)
|
|
145
|
+
multi.zadd(workspaceIndexKey(message.workspaceId), score, storageKey)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
yield* Effect.tryPromise(() => multi.exec())
|
|
149
|
+
return normalizedMessages
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
const listThreadMessagesEffect = (params: { workspaceId: string; threadId: string }) =>
|
|
153
|
+
Effect.gen(function* () {
|
|
154
|
+
const conn = redis.getConnection()
|
|
155
|
+
const storageKeys = yield* Effect.tryPromise(() =>
|
|
156
|
+
conn.zrange(threadIndexKey(params.workspaceId, params.threadId), 0, -1),
|
|
157
|
+
)
|
|
158
|
+
if (storageKeys.length === 0) return [] as SocialChatHistoryMessage[]
|
|
159
|
+
|
|
160
|
+
const storedValues = yield* Effect.tryPromise(() => conn.mget(storageKeys))
|
|
161
|
+
const parsedMessages = yield* Effect.forEach(storedValues, parseStoredMessageEffect)
|
|
162
|
+
return parsedMessages
|
|
163
|
+
.filter((message): message is SocialChatHistoryMessage => message !== null)
|
|
164
|
+
.sort((left, right) => compareCursorOrder(left.cursor, right.cursor))
|
|
165
|
+
})
|
|
128
166
|
|
|
129
|
-
|
|
167
|
+
const listWorkspaceMessagesEffect = (params: {
|
|
130
168
|
workspaceId: string
|
|
131
169
|
cursor: LotaRuntimeBackgroundCursor | null
|
|
132
170
|
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
|
-
|
|
171
|
+
}) =>
|
|
172
|
+
Effect.gen(function* () {
|
|
173
|
+
const conn = redis.getConnection()
|
|
174
|
+
const indexKey = workspaceIndexKey(params.workspaceId)
|
|
175
|
+
const scoreStart =
|
|
176
|
+
params.cursor?.createdAt.getTime() ??
|
|
177
|
+
(params.onboardingCutoff ? params.onboardingCutoff.getTime() : Number.NEGATIVE_INFINITY)
|
|
178
|
+
const storageKeys = yield* Effect.tryPromise(() =>
|
|
179
|
+
params.cursor || params.onboardingCutoff
|
|
180
|
+
? conn.zrangebyscore(indexKey, scoreStart, '+inf')
|
|
181
|
+
: conn.zrange(indexKey, 0, -1),
|
|
182
|
+
)
|
|
183
|
+
if (storageKeys.length === 0) return [] as SocialChatHistoryMessage[]
|
|
184
|
+
|
|
185
|
+
const storedValues = yield* Effect.tryPromise(() => conn.mget(storageKeys))
|
|
186
|
+
const parsedMessages = yield* Effect.forEach(storedValues, parseStoredMessageEffect)
|
|
187
|
+
return parsedMessages
|
|
188
|
+
.filter((message): message is SocialChatHistoryMessage => message !== null)
|
|
189
|
+
.filter((message) => {
|
|
190
|
+
if (params.cursor) {
|
|
191
|
+
return compareCursorOrder(message.cursor, params.cursor) > 0
|
|
192
|
+
}
|
|
193
|
+
if (params.onboardingCutoff) {
|
|
194
|
+
return message.cursor.createdAt.getTime() > params.onboardingCutoff.getTime()
|
|
195
|
+
}
|
|
196
|
+
return true
|
|
197
|
+
})
|
|
198
|
+
.sort((left, right) => compareCursorOrder(left.cursor, right.cursor))
|
|
199
|
+
})
|
|
161
200
|
|
|
162
|
-
|
|
201
|
+
const hasWorkspaceMessagesEffect = (params: {
|
|
163
202
|
workspaceId: string
|
|
164
203
|
cursor: LotaRuntimeBackgroundCursor | null
|
|
165
204
|
onboardingCutoff: Date | null
|
|
166
|
-
})
|
|
167
|
-
|
|
205
|
+
}) =>
|
|
206
|
+
listWorkspaceMessagesEffect({
|
|
168
207
|
workspaceId: params.workspaceId,
|
|
169
208
|
cursor: params.cursor,
|
|
170
209
|
onboardingCutoff: params.onboardingCutoff,
|
|
171
|
-
})
|
|
172
|
-
return messages.length > 0
|
|
173
|
-
}
|
|
210
|
+
}).pipe(Effect.map((messages) => messages.length > 0))
|
|
174
211
|
|
|
175
|
-
|
|
176
|
-
kind
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const redis = getRedisConnection()
|
|
180
|
-
return this.parseCursor(await redis.get(this.cursorKey(kind, workspaceId)))
|
|
181
|
-
}
|
|
212
|
+
const getBackgroundCursorEffect = (kind: LotaRuntimeBackgroundCursorKind, workspaceId: string) =>
|
|
213
|
+
Effect.tryPromise(() => redis.getConnection().get(cursorKey(kind, workspaceId))).pipe(
|
|
214
|
+
Effect.flatMap((value) => parseCursorEffect(value)),
|
|
215
|
+
)
|
|
182
216
|
|
|
183
|
-
|
|
217
|
+
const setBackgroundCursorEffect = (
|
|
184
218
|
kind: LotaRuntimeBackgroundCursorKind,
|
|
185
219
|
workspaceId: string,
|
|
186
220
|
cursor: LotaRuntimeBackgroundCursor,
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
|
|
221
|
+
) =>
|
|
222
|
+
Effect.tryPromise(() => redis.getConnection().set(cursorKey(kind, workspaceId), serializeCursor(cursor))).pipe(
|
|
223
|
+
Effect.asVoid,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
upsertMessages: upsertMessagesEffect,
|
|
228
|
+
listThreadMessages: listThreadMessagesEffect,
|
|
229
|
+
listWorkspaceMessages: listWorkspaceMessagesEffect,
|
|
230
|
+
hasWorkspaceMessages: hasWorkspaceMessagesEffect,
|
|
231
|
+
getBackgroundCursor: getBackgroundCursorEffect,
|
|
232
|
+
setBackgroundCursor: setBackgroundCursorEffect,
|
|
190
233
|
}
|
|
191
234
|
}
|
|
192
235
|
|
|
193
|
-
export
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
236
|
+
export class SocialChatHistoryServiceTag extends Context.Service<
|
|
237
|
+
SocialChatHistoryServiceTag,
|
|
238
|
+
ReturnType<typeof makeSocialChatHistoryService>
|
|
239
|
+
>()('SocialChatHistoryService') {}
|
|
240
|
+
|
|
241
|
+
export const SocialChatHistoryServiceLive = Layer.effect(
|
|
242
|
+
SocialChatHistoryServiceTag,
|
|
243
|
+
Effect.gen(function* () {
|
|
244
|
+
const redis = yield* RedisServiceTag
|
|
245
|
+
const config = yield* RuntimeConfigServiceTag
|
|
246
|
+
return makeSocialChatHistoryService(redis, config)
|
|
247
|
+
}),
|
|
248
|
+
)
|
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
import type { OwnershipDispatchContext, PlanNodeResult, PlanNodeSpec, SystemPlanNodeOwner } from '@lota-sdk/shared'
|
|
2
|
+
import { Context, Effect, Layer } from 'effect'
|
|
2
3
|
|
|
4
|
+
import { BadRequestError } from '../effect/errors'
|
|
5
|
+
import { RuntimeConfigServiceTag } from '../effect/services'
|
|
3
6
|
import type { SystemNodeExecutor, PluginNodeExecutionParams } from '../runtime/plugin-types'
|
|
4
|
-
import {
|
|
5
|
-
import type { PlanValidationIssueInput } from './plan-validator.service'
|
|
7
|
+
import type { ResolvedLotaRuntimeConfig } from '../runtime/runtime-config'
|
|
8
|
+
import type { PlanValidationIssueInput } from './plan/plan-validator.service'
|
|
6
9
|
|
|
7
10
|
const BUILT_IN_SYSTEM_EXECUTORS = Object.freeze({
|
|
8
11
|
'plan-runtime': {
|
|
9
12
|
supportedOperations: ['echo-input'],
|
|
10
|
-
|
|
11
|
-
return
|
|
13
|
+
executeNode(params: PluginNodeExecutionParams): Promise<PlanNodeResult> {
|
|
14
|
+
return Promise.resolve({
|
|
15
|
+
notes: 'System echo-input completed.',
|
|
16
|
+
structuredOutput: structuredClone(params.inputs),
|
|
17
|
+
artifacts: [],
|
|
18
|
+
})
|
|
12
19
|
},
|
|
13
20
|
} satisfies SystemNodeExecutor,
|
|
14
21
|
})
|
|
15
22
|
|
|
16
|
-
|
|
17
|
-
|
|
23
|
+
function isSystemOwner(
|
|
24
|
+
owner: PlanNodeSpec['owner'],
|
|
25
|
+
): owner is Extract<PlanNodeSpec['owner'], { executorType: 'system' }> {
|
|
26
|
+
return owner.executorType === 'system'
|
|
18
27
|
}
|
|
19
28
|
|
|
20
|
-
function
|
|
21
|
-
return (
|
|
22
|
-
string,
|
|
23
|
-
SystemNodeExecutor | undefined
|
|
24
|
-
>
|
|
29
|
+
export function getBuiltInSystemExecutors(): Record<string, SystemNodeExecutor> {
|
|
30
|
+
return Object.fromEntries(Object.entries(BUILT_IN_SYSTEM_EXECUTORS))
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
function buildSystemExecutionParams(params: {
|
|
@@ -45,61 +51,85 @@ function buildSystemExecutionParams(params: {
|
|
|
45
51
|
}
|
|
46
52
|
}
|
|
47
53
|
|
|
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
|
-
}
|
|
54
|
+
export function makeSystemExecutorService(config: ResolvedLotaRuntimeConfig) {
|
|
55
|
+
function getSystemExecutors() {
|
|
56
|
+
return (config.systemExecutors ?? getBuiltInSystemExecutors()) as Record<string, SystemNodeExecutor | undefined>
|
|
57
|
+
}
|
|
62
58
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
59
|
+
return {
|
|
60
|
+
validateOwner(owner: SystemPlanNodeOwner, nodeId: string): PlanValidationIssueInput[] {
|
|
61
|
+
const executor = getSystemExecutors()[owner.ref]
|
|
62
|
+
if (!executor) {
|
|
63
|
+
return [
|
|
64
|
+
{
|
|
65
|
+
severity: 'blocking',
|
|
66
|
+
code: 'system_executor_missing',
|
|
67
|
+
message: `Node "${nodeId}" references unknown system executor "${owner.ref}".`,
|
|
68
|
+
nodeId,
|
|
69
|
+
detail: { systemRef: owner.ref },
|
|
70
|
+
},
|
|
71
|
+
]
|
|
72
|
+
}
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
if (!executor.supportedOperations.includes(owner.operation)) {
|
|
75
|
+
return [
|
|
76
|
+
{
|
|
77
|
+
severity: 'blocking',
|
|
78
|
+
code: 'system_operation_missing',
|
|
79
|
+
message: `System executor "${owner.ref}" does not support operation "${owner.operation}".`,
|
|
80
|
+
nodeId,
|
|
81
|
+
detail: { systemRef: owner.ref, operation: owner.operation },
|
|
82
|
+
},
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return []
|
|
87
|
+
},
|
|
77
88
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
executeNode(params: {
|
|
90
|
+
nodeSpec: PlanNodeSpec
|
|
91
|
+
resolvedInput: Record<string, unknown>
|
|
92
|
+
context: OwnershipDispatchContext
|
|
93
|
+
}) {
|
|
94
|
+
return Effect.gen(function* () {
|
|
95
|
+
const owner = params.nodeSpec.owner
|
|
96
|
+
if (!isSystemOwner(owner)) {
|
|
97
|
+
return yield* new BadRequestError({
|
|
98
|
+
message: `SystemExecutor cannot execute owner type "${owner.executorType}".`,
|
|
99
|
+
})
|
|
100
|
+
}
|
|
86
101
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
102
|
+
const executor = getSystemExecutors()[owner.ref]
|
|
103
|
+
if (!executor || !executor.supportedOperations.includes(owner.operation)) {
|
|
104
|
+
return yield* new BadRequestError({
|
|
105
|
+
message: `System executor ${owner.ref}.${owner.operation} is not registered.`,
|
|
106
|
+
})
|
|
107
|
+
}
|
|
93
108
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
109
|
+
return yield* Effect.tryPromise(() =>
|
|
110
|
+
executor.executeNode(
|
|
111
|
+
buildSystemExecutionParams({
|
|
112
|
+
owner,
|
|
113
|
+
nodeSpec: params.nodeSpec,
|
|
114
|
+
resolvedInput: params.resolvedInput,
|
|
115
|
+
context: params.context,
|
|
116
|
+
}),
|
|
117
|
+
),
|
|
118
|
+
)
|
|
119
|
+
})
|
|
120
|
+
},
|
|
102
121
|
}
|
|
103
122
|
}
|
|
104
123
|
|
|
105
|
-
export
|
|
124
|
+
export class SystemExecutorServiceTag extends Context.Service<
|
|
125
|
+
SystemExecutorServiceTag,
|
|
126
|
+
ReturnType<typeof makeSystemExecutorService>
|
|
127
|
+
>()('SystemExecutorService') {}
|
|
128
|
+
|
|
129
|
+
export const SystemExecutorServiceLive = Layer.effect(
|
|
130
|
+
SystemExecutorServiceTag,
|
|
131
|
+
Effect.gen(function* () {
|
|
132
|
+
const runtimeConfig = yield* RuntimeConfigServiceTag
|
|
133
|
+
return makeSystemExecutorService(runtimeConfig)
|
|
134
|
+
}),
|
|
135
|
+
)
|