@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,12 +1,18 @@
|
|
|
1
|
+
import type { Context, Cause } from 'effect'
|
|
2
|
+
import { Effect } from 'effect'
|
|
3
|
+
|
|
1
4
|
import { serverLogger } from '../config/logger'
|
|
2
5
|
import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
6
|
+
import type { RecordIdRef } from '../db/record-id'
|
|
7
|
+
import type { SurrealDBService } from '../db/service'
|
|
3
8
|
import { TABLES } from '../db/tables'
|
|
4
|
-
import {
|
|
9
|
+
import { effectTryPromise } from '../effect/helpers'
|
|
10
|
+
import { ProviderEmbeddings } from '../embeddings/provider'
|
|
5
11
|
import type { SkillExtractionJob } from '../queues/organization-learning.queue'
|
|
6
12
|
import { createHelperModelRuntime } from '../runtime/helper-model'
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
13
|
+
import type { LotaRuntimeAdapters } from '../runtime/runtime-extensions'
|
|
14
|
+
import type { LearnedSkillServiceTag } from '../services/learned-skill.service'
|
|
15
|
+
import type { SocialChatHistoryServiceTag } from '../services/social-chat-history.service'
|
|
10
16
|
import { createSkillExtractorAgent, SkillExtractionOutputSchema } from '../system-agents/skill-extractor.agent'
|
|
11
17
|
import type { SkillCandidate } from '../system-agents/skill-extractor.agent'
|
|
12
18
|
import { createSkillManagerAgent, SkillManagerOutputSchema } from '../system-agents/skill-manager.agent'
|
|
@@ -20,16 +26,36 @@ import {
|
|
|
20
26
|
const SKILL_EXTRACTION_TIMEOUT_MS = 10 * 60 * 1000
|
|
21
27
|
const MIN_MESSAGE_THRESHOLD = 10
|
|
22
28
|
|
|
29
|
+
export interface SkillExtractionServices {
|
|
30
|
+
databaseService: SurrealDBService
|
|
31
|
+
learnedSkillService: Context.Service.Shape<typeof LearnedSkillServiceTag>
|
|
32
|
+
socialChatHistoryService: Context.Service.Shape<typeof SocialChatHistoryServiceTag>
|
|
33
|
+
runtimeAdapters: LotaRuntimeAdapters
|
|
34
|
+
embeddingModel: string
|
|
35
|
+
openRouterApiKey?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
23
38
|
interface SkillExtractionRunResult {
|
|
24
39
|
skipped: boolean
|
|
25
40
|
processedMessages: number
|
|
26
41
|
extractedSkills: number
|
|
27
42
|
}
|
|
28
43
|
|
|
29
|
-
const embeddings = getDefaultEmbeddings()
|
|
30
|
-
|
|
31
44
|
const helperModelRuntime = createHelperModelRuntime()
|
|
32
45
|
|
|
46
|
+
type CursorAwareWorkspaceProvider = NonNullable<LotaRuntimeAdapters['workspaceProvider']> & {
|
|
47
|
+
getBackgroundCursor: NonNullable<NonNullable<LotaRuntimeAdapters['workspaceProvider']>['getBackgroundCursor']>
|
|
48
|
+
setBackgroundCursor: NonNullable<NonNullable<LotaRuntimeAdapters['workspaceProvider']>['setBackgroundCursor']>
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function hasCursorAwareWorkspaceProvider(
|
|
52
|
+
workspaceProvider: LotaRuntimeAdapters['workspaceProvider'],
|
|
53
|
+
): workspaceProvider is CursorAwareWorkspaceProvider {
|
|
54
|
+
const getBackgroundCursor = workspaceProvider?.getBackgroundCursor?.bind(workspaceProvider)
|
|
55
|
+
const setBackgroundCursor = workspaceProvider?.setBackgroundCursor?.bind(workspaceProvider)
|
|
56
|
+
return Boolean(getBackgroundCursor && setBackgroundCursor)
|
|
57
|
+
}
|
|
58
|
+
|
|
33
59
|
function buildExtractionPrompt(params: { workspaceName: string; transcript: string; existingSkills: string }): string {
|
|
34
60
|
return [
|
|
35
61
|
`Workspace name: ${params.workspaceName}`,
|
|
@@ -71,45 +97,46 @@ function buildManagerPrompt(params: {
|
|
|
71
97
|
return parts.join('\n')
|
|
72
98
|
}
|
|
73
99
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
100
|
+
function runSkillExtractionEffect(
|
|
101
|
+
services: SkillExtractionServices,
|
|
102
|
+
orgRef: RecordIdRef,
|
|
103
|
+
orgId: string,
|
|
104
|
+
cursorAwareWorkspaceProvider: CursorAwareWorkspaceProvider,
|
|
105
|
+
embeddings: ProviderEmbeddings,
|
|
106
|
+
) {
|
|
107
|
+
return Effect.gen(function* () {
|
|
108
|
+
const getBackgroundCursor = cursorAwareWorkspaceProvider.getBackgroundCursor.bind(cursorAwareWorkspaceProvider)
|
|
109
|
+
const setBackgroundCursor = cursorAwareWorkspaceProvider.setBackgroundCursor.bind(cursorAwareWorkspaceProvider)
|
|
110
|
+
const workspace = yield* effectTryPromise(() => cursorAwareWorkspaceProvider.getWorkspace(orgRef))
|
|
111
|
+
const lifecycleState = cursorAwareWorkspaceProvider.getLifecycleState
|
|
112
|
+
? yield* effectTryPromise(() => Promise.resolve(cursorAwareWorkspaceProvider.getLifecycleState?.(workspace)))
|
|
84
113
|
: undefined
|
|
85
|
-
if (!cursorAwareWorkspaceProvider) {
|
|
86
|
-
serverLogger.info`Skipping skill extraction for ${orgId}: workspaceProvider background cursor methods are not configured`
|
|
87
|
-
return { skipped: true, processedMessages: 0, extractedSkills: 0 }
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return withConfiguredWorkspaceMemoryLock(orgId, async () => {
|
|
91
|
-
const workspace = await cursorAwareWorkspaceProvider.getWorkspace(orgRef)
|
|
92
|
-
const lifecycleState = await cursorAwareWorkspaceProvider.getLifecycleState?.(workspace)
|
|
93
114
|
if (lifecycleState?.bootstrapActive ?? false) {
|
|
94
115
|
serverLogger.info`Skipping skill extraction for ${orgId}: onboarding is not completed`
|
|
95
116
|
return { skipped: true, processedMessages: 0, extractedSkills: 0 }
|
|
96
117
|
}
|
|
97
|
-
const projectionState =
|
|
118
|
+
const projectionState = cursorAwareWorkspaceProvider.readProfileProjectionState
|
|
119
|
+
? yield* effectTryPromise(() =>
|
|
120
|
+
Promise.resolve(cursorAwareWorkspaceProvider.readProfileProjectionState?.(workspace)),
|
|
121
|
+
)
|
|
122
|
+
: undefined
|
|
98
123
|
|
|
99
|
-
const existingCursor =
|
|
124
|
+
const existingCursor = yield* effectTryPromise(() => getBackgroundCursor('skill-extraction', orgRef))
|
|
100
125
|
const onboardingCutoff = resolveWorkspaceBootstrapCutoff({
|
|
101
126
|
hasExistingCursor: existingCursor !== null,
|
|
102
127
|
bootstrapCompletedAt: lifecycleState?.bootstrapCompletedAt,
|
|
103
128
|
})
|
|
104
|
-
const existingSocialCursor =
|
|
129
|
+
const existingSocialCursor = yield* services.socialChatHistoryService.getBackgroundCursor('skill-extraction', orgId)
|
|
105
130
|
const socialOnboardingCutoff = resolveWorkspaceBootstrapCutoff({
|
|
106
131
|
hasExistingCursor: existingSocialCursor !== null,
|
|
107
132
|
bootstrapCompletedAt: lifecycleState?.bootstrapCompletedAt,
|
|
108
133
|
})
|
|
109
134
|
|
|
110
|
-
const threadIds =
|
|
111
|
-
const threadMessages =
|
|
112
|
-
|
|
135
|
+
const threadIds = yield* effectTryPromise(() => listThreadIdsForOrg(services.databaseService, orgRef))
|
|
136
|
+
const threadMessages = yield* effectTryPromise(() =>
|
|
137
|
+
listEligibleThreadMessages({ db: services.databaseService, threadIds, cursor: existingCursor, onboardingCutoff }),
|
|
138
|
+
)
|
|
139
|
+
const socialMessages = yield* services.socialChatHistoryService.listWorkspaceMessages({
|
|
113
140
|
workspaceId: orgId,
|
|
114
141
|
cursor: existingSocialCursor,
|
|
115
142
|
onboardingCutoff: socialOnboardingCutoff,
|
|
@@ -124,79 +151,82 @@ export async function runSkillExtraction(data: SkillExtractionJob): Promise<Skil
|
|
|
124
151
|
const sortedMessages = [...messages].sort(compareDigestMessageOrder)
|
|
125
152
|
const { transcript } = buildDigestTranscript({ messages: sortedMessages })
|
|
126
153
|
|
|
127
|
-
const existingSkills =
|
|
154
|
+
const existingSkills = yield* services.learnedSkillService.listForOrg(orgId)
|
|
128
155
|
const existingSkillsSummary =
|
|
129
156
|
existingSkills.length > 0
|
|
130
157
|
? existingSkills.map((skill, i) => `${i + 1}. ${skill.name}: ${skill.description}`).join('\n')
|
|
131
158
|
: 'None'
|
|
132
159
|
|
|
133
|
-
const extraction =
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
160
|
+
const extraction = yield* effectTryPromise(() =>
|
|
161
|
+
helperModelRuntime.generateHelperStructured({
|
|
162
|
+
tag: 'skill-extraction',
|
|
163
|
+
createAgent: createSkillExtractorAgent,
|
|
164
|
+
timeoutMs: SKILL_EXTRACTION_TIMEOUT_MS,
|
|
165
|
+
messages: [
|
|
166
|
+
{
|
|
167
|
+
role: 'user',
|
|
168
|
+
content: buildExtractionPrompt({
|
|
169
|
+
workspaceName: projectionState?.workspaceName || 'Workspace',
|
|
170
|
+
transcript,
|
|
171
|
+
existingSkills: existingSkillsSummary,
|
|
172
|
+
}),
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
schema: SkillExtractionOutputSchema,
|
|
176
|
+
}),
|
|
177
|
+
)
|
|
149
178
|
|
|
150
179
|
const skillCandidates = extraction.candidates.filter((c) => c.classification === 'skill')
|
|
151
|
-
let extractedSkills = 0
|
|
152
180
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const hash = learnedSkillService.generateHash(candidate.description, candidate.instructions)
|
|
156
|
-
const existingByHash =
|
|
157
|
-
if (existingByHash) {
|
|
181
|
+
const processCandidate = (candidate: SkillCandidate): Effect.Effect<number, Cause.UnknownError, never> => {
|
|
182
|
+
const processCandidateEffect = Effect.gen(function* () {
|
|
183
|
+
const hash = services.learnedSkillService.generateHash(candidate.description, candidate.instructions)
|
|
184
|
+
const existingByHash = yield* services.learnedSkillService.findByHash(orgId, hash)
|
|
185
|
+
if (existingByHash !== null) {
|
|
158
186
|
serverLogger.info`Skipping duplicate skill candidate ${candidate.name} (hash match)`
|
|
159
|
-
|
|
187
|
+
return 0
|
|
160
188
|
}
|
|
161
189
|
|
|
162
|
-
const mostSimilar =
|
|
163
|
-
|
|
164
|
-
const managerResult =
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
190
|
+
const mostSimilar = yield* services.learnedSkillService.findMostSimilar(orgId, candidate.description)
|
|
191
|
+
|
|
192
|
+
const managerResult = yield* effectTryPromise(() =>
|
|
193
|
+
helperModelRuntime.generateHelperStructured({
|
|
194
|
+
tag: 'skill-manager',
|
|
195
|
+
createAgent: createSkillManagerAgent,
|
|
196
|
+
timeoutMs: SKILL_EXTRACTION_TIMEOUT_MS,
|
|
197
|
+
messages: [
|
|
198
|
+
{
|
|
199
|
+
role: 'user',
|
|
200
|
+
content: buildManagerPrompt({
|
|
201
|
+
candidate,
|
|
202
|
+
existingSkill: mostSimilar
|
|
203
|
+
? {
|
|
204
|
+
name: mostSimilar.name,
|
|
205
|
+
description: mostSimilar.description,
|
|
206
|
+
instructions: mostSimilar.instructions,
|
|
207
|
+
version: mostSimilar.version,
|
|
208
|
+
}
|
|
209
|
+
: null,
|
|
210
|
+
}),
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
schema: SkillManagerOutputSchema,
|
|
214
|
+
}),
|
|
215
|
+
)
|
|
186
216
|
|
|
187
217
|
if (managerResult.decision === 'discard') {
|
|
188
218
|
serverLogger.info`Discarding skill candidate ${candidate.name}: ${managerResult.reason}`
|
|
189
|
-
|
|
219
|
+
return 0
|
|
190
220
|
}
|
|
191
221
|
|
|
192
|
-
const embedding =
|
|
222
|
+
const embedding = yield* effectTryPromise(() => embeddings.embedQuery(candidate.description))
|
|
193
223
|
if (embedding.length === 0) {
|
|
194
224
|
serverLogger.warn`Skipping skill candidate ${candidate.name}: empty embedding`
|
|
195
|
-
|
|
225
|
+
return 0
|
|
196
226
|
}
|
|
197
227
|
|
|
198
228
|
if (managerResult.decision === 'add') {
|
|
199
|
-
|
|
229
|
+
yield* services.learnedSkillService.create({
|
|
200
230
|
name: candidate.name,
|
|
201
231
|
description: candidate.description,
|
|
202
232
|
instructions: candidate.instructions,
|
|
@@ -210,14 +240,16 @@ export async function runSkillExtraction(data: SkillExtractionJob): Promise<Skil
|
|
|
210
240
|
embedding,
|
|
211
241
|
hash,
|
|
212
242
|
})
|
|
213
|
-
extractedSkills++
|
|
214
243
|
serverLogger.info`Added new learned skill: ${candidate.name}`
|
|
215
|
-
|
|
244
|
+
return 1
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (mostSimilar && managerResult.mergedSkill) {
|
|
216
248
|
const merged = managerResult.mergedSkill
|
|
217
|
-
const mergedHash = learnedSkillService.generateHash(merged.description, merged.instructions)
|
|
218
|
-
const mergedEmbedding =
|
|
249
|
+
const mergedHash = services.learnedSkillService.generateHash(merged.description, merged.instructions)
|
|
250
|
+
const mergedEmbedding = yield* effectTryPromise(() => embeddings.embedQuery(merged.description))
|
|
219
251
|
|
|
220
|
-
|
|
252
|
+
yield* services.learnedSkillService.update(mostSimilar.id, {
|
|
221
253
|
name: merged.name,
|
|
222
254
|
description: merged.description,
|
|
223
255
|
instructions: merged.instructions,
|
|
@@ -229,21 +261,34 @@ export async function runSkillExtraction(data: SkillExtractionJob): Promise<Skil
|
|
|
229
261
|
embedding: mergedEmbedding,
|
|
230
262
|
hash: mergedHash,
|
|
231
263
|
})
|
|
232
|
-
extractedSkills++
|
|
233
264
|
serverLogger.info`Merged skill candidate into ${mostSimilar.name} (v${mostSimilar.version + 1})`
|
|
265
|
+
return 1
|
|
234
266
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
267
|
+
|
|
268
|
+
return 0
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
return Effect.matchCauseEffect(processCandidateEffect, {
|
|
272
|
+
onFailure: (cause) =>
|
|
273
|
+
Effect.sync(() => {
|
|
274
|
+
serverLogger.warn`Failed to process skill candidate ${candidate.name}: ${cause}`
|
|
275
|
+
return 0
|
|
276
|
+
}),
|
|
277
|
+
onSuccess: (value) => Effect.succeed(value),
|
|
278
|
+
})
|
|
238
279
|
}
|
|
280
|
+
const extractedSkills = (yield* Effect.forEach(skillCandidates, processCandidate, { concurrency: 2 })).reduce(
|
|
281
|
+
(sum, n) => sum + n,
|
|
282
|
+
0,
|
|
283
|
+
)
|
|
239
284
|
|
|
240
285
|
const lastThreadMessage = threadMessages.at(-1)
|
|
241
286
|
const lastSocialMessage = socialMessages.at(-1)
|
|
242
287
|
if (lastThreadMessage) {
|
|
243
|
-
|
|
288
|
+
yield* effectTryPromise(() => setBackgroundCursor('skill-extraction', orgRef, lastThreadMessage.cursor))
|
|
244
289
|
}
|
|
245
290
|
if (lastSocialMessage) {
|
|
246
|
-
|
|
291
|
+
yield* services.socialChatHistoryService.setBackgroundCursor('skill-extraction', orgId, lastSocialMessage.cursor)
|
|
247
292
|
}
|
|
248
293
|
|
|
249
294
|
serverLogger.info`Skill extraction completed for ${orgId}: messages=${messages.length}, socialMessages=${socialMessages.length}, extracted=${extractedSkills}`
|
|
@@ -251,3 +296,41 @@ export async function runSkillExtraction(data: SkillExtractionJob): Promise<Skil
|
|
|
251
296
|
return { skipped: false, processedMessages: messages.length, extractedSkills }
|
|
252
297
|
})
|
|
253
298
|
}
|
|
299
|
+
|
|
300
|
+
export function runSkillExtraction(
|
|
301
|
+
data: SkillExtractionJob,
|
|
302
|
+
services: SkillExtractionServices,
|
|
303
|
+
): Promise<SkillExtractionRunResult> {
|
|
304
|
+
const { databaseService, learnedSkillService, socialChatHistoryService, runtimeAdapters } = services
|
|
305
|
+
const orgRef = ensureRecordId(data.orgId, TABLES.ORGANIZATION)
|
|
306
|
+
const orgId = recordIdToString(orgRef, TABLES.ORGANIZATION)
|
|
307
|
+
const workspaceProvider = runtimeAdapters.workspaceProvider
|
|
308
|
+
if (!hasCursorAwareWorkspaceProvider(workspaceProvider)) {
|
|
309
|
+
serverLogger.info`Skipping skill extraction for ${orgId}: workspaceProvider background cursor methods are not configured`
|
|
310
|
+
return Promise.resolve({ skipped: true, processedMessages: 0, extractedSkills: 0 })
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const embeddings = new ProviderEmbeddings({
|
|
314
|
+
modelId: services.embeddingModel,
|
|
315
|
+
openRouterApiKey: services.openRouterApiKey,
|
|
316
|
+
})
|
|
317
|
+
const withMemoryLock = runtimeAdapters.withWorkspaceMemoryLock
|
|
318
|
+
const runExtraction = () =>
|
|
319
|
+
Effect.runPromise(
|
|
320
|
+
runSkillExtractionEffect(
|
|
321
|
+
{
|
|
322
|
+
databaseService,
|
|
323
|
+
learnedSkillService,
|
|
324
|
+
socialChatHistoryService,
|
|
325
|
+
runtimeAdapters,
|
|
326
|
+
embeddingModel: services.embeddingModel,
|
|
327
|
+
},
|
|
328
|
+
orgRef,
|
|
329
|
+
orgId,
|
|
330
|
+
workspaceProvider,
|
|
331
|
+
embeddings,
|
|
332
|
+
),
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
return withMemoryLock ? withMemoryLock(orgId, runExtraction) : runExtraction()
|
|
336
|
+
}
|
|
@@ -50,7 +50,7 @@ function normalizeMinChunkChars(value: number | undefined, maxChars: number): nu
|
|
|
50
50
|
return Math.min(normalized, Math.floor(maxChars * 0.6))
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
function splitTextByCharBudget(text: string, maxChars: number): string[] {
|
|
54
54
|
const source = text.trim()
|
|
55
55
|
if (!source) return []
|
|
56
56
|
if (source.length <= maxChars) return [source]
|
|
@@ -157,16 +157,16 @@ function mergeTinyTailParts(parts: string[], options: { minChunkChars: number; m
|
|
|
157
157
|
return merged
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
|
|
160
|
+
function splitOversizedSection(
|
|
161
161
|
section: FileSection,
|
|
162
162
|
options: { maxChars: number; minChunkChars: number; preserveCodeFenceIntegrity: boolean },
|
|
163
|
-
):
|
|
163
|
+
): FileSection[] {
|
|
164
164
|
if (section.content.length <= options.maxChars) {
|
|
165
165
|
return [section]
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
if (section.kind !== 'file' || !section.filePath) {
|
|
169
|
-
const chunks =
|
|
169
|
+
const chunks = splitTextByCharBudget(section.content, options.maxChars)
|
|
170
170
|
return chunks.map((content) => ({ kind: section.kind, content }))
|
|
171
171
|
}
|
|
172
172
|
|
|
@@ -223,10 +223,10 @@ function mergeTinyChunks(
|
|
|
223
223
|
return merged
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
-
export
|
|
226
|
+
export function chunkFileSections(
|
|
227
227
|
fileSections: readonly FileSection[],
|
|
228
228
|
options: FileSectionChunkOptions = {},
|
|
229
|
-
):
|
|
229
|
+
): FileSectionChunk[] {
|
|
230
230
|
const maxChars = normalizeMaxChars(options.maxChars)
|
|
231
231
|
const minChunkChars = normalizeMinChunkChars(options.minChunkChars, maxChars)
|
|
232
232
|
const preserveCodeFenceIntegrity = options.preserveCodeFenceIntegrity ?? true
|
|
@@ -235,10 +235,8 @@ export async function chunkFileSections(
|
|
|
235
235
|
.map((section) => ({ kind: section.kind, content: section.content.trim(), filePath: section.filePath }))
|
|
236
236
|
.filter((section) => section.content.length > 0)
|
|
237
237
|
|
|
238
|
-
const splitSections =
|
|
239
|
-
|
|
240
|
-
async (section) => await splitOversizedSection(section, { maxChars, minChunkChars, preserveCodeFenceIntegrity }),
|
|
241
|
-
),
|
|
238
|
+
const splitSections = rawSections.map((section) =>
|
|
239
|
+
splitOversizedSection(section, { maxChars, minChunkChars, preserveCodeFenceIntegrity }),
|
|
242
240
|
)
|
|
243
241
|
const sections = splitSections.flat()
|
|
244
242
|
|