@lota-sdk/core 0.4.10 → 0.4.12
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 +3 -3
- package/src/ai-gateway/ai-gateway.ts +214 -98
- package/src/ai-gateway/index.ts +16 -1
- package/src/config/agent-defaults.ts +4 -120
- package/src/config/logger.ts +18 -34
- package/src/config/model-constants.ts +1 -0
- package/src/config/thread-defaults.ts +1 -18
- package/src/create-runtime.ts +90 -28
- package/src/db/base.service.ts +30 -38
- package/src/db/service.ts +489 -545
- package/src/effect/index.ts +0 -2
- package/src/effect/layers.ts +6 -13
- package/src/embeddings/provider.ts +2 -7
- package/src/index.ts +4 -5
- package/src/queues/autonomous-job.queue.ts +159 -113
- package/src/queues/context-compaction.queue.ts +39 -25
- package/src/queues/delayed-node-promotion.queue.ts +56 -29
- package/src/queues/document-processor.queue.ts +5 -3
- package/src/queues/index.ts +1 -0
- package/src/queues/memory-consolidation.queue.ts +79 -53
- package/src/queues/organization-learning.queue.ts +63 -39
- package/src/queues/plan-agent-heartbeat.queue.ts +104 -79
- package/src/queues/plan-scheduler.queue.ts +100 -84
- package/src/queues/post-chat-memory.queue.ts +55 -33
- package/src/queues/queue-factory.ts +40 -41
- package/src/queues/queues.service.ts +61 -0
- package/src/queues/title-generation.queue.ts +42 -31
- package/src/redis/org-memory-lock.ts +24 -9
- package/src/redis/redis-lease-lock.ts +8 -1
- package/src/runtime/agent-identity-overrides.ts +7 -3
- package/src/runtime/agent-runtime-policy.ts +9 -4
- package/src/runtime/agent-stream-helpers.ts +9 -4
- package/src/runtime/context-compaction/context-compaction-runtime.ts +28 -32
- package/src/runtime/context-compaction/context-compaction.ts +9 -7
- package/src/runtime/domain-layer.ts +15 -4
- package/src/runtime/execution-plan-visibility.ts +5 -2
- package/src/runtime/graph-designer.ts +0 -22
- package/src/runtime/index.ts +2 -0
- package/src/runtime/indexed-repositories-policy.ts +2 -6
- package/src/runtime/live-turn-trace.ts +344 -0
- package/src/runtime/plugin-resolution.ts +29 -12
- package/src/runtime/post-turn-side-effects.ts +139 -141
- package/src/runtime/runtime-config.ts +0 -6
- package/src/runtime/runtime-extensions.ts +0 -54
- package/src/runtime/runtime-lifecycle.ts +4 -4
- package/src/runtime/runtime-services.ts +125 -53
- package/src/runtime/runtime-worker-registry.ts +113 -30
- package/src/runtime/social-chat/social-chat-agent-runner.ts +6 -3
- package/src/runtime/social-chat/social-chat-history.ts +3 -1
- package/src/runtime/social-chat/social-chat.ts +35 -20
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +6 -5
- package/src/runtime/team-consultation/team-consultation-prompts.ts +11 -6
- package/src/runtime/thread-chat-helpers.ts +18 -9
- package/src/runtime/thread-turn-context.ts +7 -47
- package/src/runtime/turn-lifecycle.ts +6 -14
- package/src/services/agent-activity.service.ts +168 -175
- package/src/services/agent-executor.service.ts +35 -16
- package/src/services/attachment.service.ts +4 -70
- package/src/services/autonomous-job.service.ts +53 -61
- package/src/services/context-compaction.service.ts +7 -9
- package/src/services/execution-plan/execution-plan-graph.ts +106 -115
- package/src/services/execution-plan/execution-plan-schedule.ts +1 -15
- package/src/services/execution-plan/execution-plan.service.ts +67 -50
- package/src/services/global-orchestrator.service.ts +18 -7
- package/src/services/graph-full-routing.ts +7 -6
- package/src/services/memory/memory-conversation.ts +10 -5
- package/src/services/memory/memory.service.ts +11 -8
- package/src/services/ownership-dispatcher.service.ts +16 -5
- package/src/services/plan/plan-agent-heartbeat.service.ts +29 -15
- package/src/services/plan/plan-agent-query.service.ts +12 -8
- package/src/services/plan/plan-completion-side-effects.ts +93 -101
- package/src/services/plan/plan-cycle.service.ts +7 -45
- package/src/services/plan/plan-deadline.service.ts +28 -17
- package/src/services/plan/plan-event-delivery.service.ts +47 -40
- package/src/services/plan/plan-executor-context.ts +2 -0
- package/src/services/plan/plan-executor-graph.ts +366 -391
- package/src/services/plan/plan-executor.service.ts +13 -91
- package/src/services/plan/plan-scheduler.service.ts +62 -49
- package/src/services/plan/plan-transaction-events.ts +1 -1
- package/src/services/recent-activity-title.service.ts +6 -2
- package/src/services/thread/thread-bootstrap.ts +11 -9
- package/src/services/thread/thread-message.service.ts +6 -5
- package/src/services/thread/thread-turn-execution.ts +86 -82
- package/src/services/thread/thread-turn-preparation.service.ts +92 -45
- package/src/services/thread/thread-turn-streaming.ts +60 -28
- package/src/services/thread/thread-turn.ts +212 -46
- package/src/services/thread/thread.service.ts +21 -6
- package/src/system-agents/recent-activity-title-refiner.agent.ts +8 -5
- package/src/system-agents/thread-router.agent.ts +23 -20
- package/src/tools/execution-plan.tool.ts +8 -3
- package/src/tools/fetch-webpage.tool.ts +10 -9
- package/src/tools/firecrawl-client.ts +0 -15
- package/src/tools/remember-memory.tool.ts +3 -6
- package/src/tools/research-topic.tool.ts +12 -3
- package/src/tools/search-web.tool.ts +10 -9
- package/src/tools/search.tool.ts +4 -5
- package/src/tools/team-think.tool.ts +139 -121
- package/src/workers/bootstrap.ts +9 -10
- package/src/workers/memory-consolidation.worker.ts +4 -1
- package/src/workers/organization-learning.worker.ts +15 -2
- package/src/workers/regular-chat-memory-digest.helpers.ts +3 -4
- package/src/workers/regular-chat-memory-digest.runner.ts +21 -14
- package/src/workers/skill-extraction.runner.ts +13 -15
- package/src/workers/worker-utils.ts +6 -18
- package/src/effect/awaitable-effect.ts +0 -96
- package/src/effect/runtime-ref.ts +0 -25
- package/src/effect/runtime.ts +0 -46
- package/src/redis/runtime-connection.ts +0 -20
- package/src/runtime/runtime-accessors.ts +0 -92
- package/src/runtime/runtime-token.ts +0 -47
|
@@ -5,8 +5,10 @@ import type IORedis from 'ioredis'
|
|
|
5
5
|
|
|
6
6
|
import { ensureRecordId } from '../db/record-id'
|
|
7
7
|
import { DatabaseServiceTag, RedisServiceTag } from '../effect/services'
|
|
8
|
+
import { QueueJobServiceTag } from '../services/queue-job.service'
|
|
8
9
|
import { RecentActivityTitleServiceTag } from '../services/recent-activity-title.service'
|
|
9
10
|
import { ThreadTitleServiceTag } from '../services/thread/thread-title.service'
|
|
11
|
+
import type { QueueJobService, WorkerHandle } from '../workers/worker-utils'
|
|
10
12
|
import { createQueueFactoryWithDeps } from './queue-factory'
|
|
11
13
|
import { runStandaloneQueueWorker } from './standalone-worker'
|
|
12
14
|
|
|
@@ -29,13 +31,13 @@ interface RecentActivityTitleRefinementJob {
|
|
|
29
31
|
|
|
30
32
|
type TitleGenerationJob = ThreadTitleGenerationJob | RecentActivityTitleRefinementJob
|
|
31
33
|
|
|
32
|
-
interface
|
|
34
|
+
export interface TitleGenerationWorkerDeps {
|
|
33
35
|
databaseService: Context.Service.Shape<typeof DatabaseServiceTag>
|
|
34
36
|
threadTitleService: Context.Service.Shape<typeof ThreadTitleServiceTag>
|
|
35
37
|
recentActivityTitleService: Context.Service.Shape<typeof RecentActivityTitleServiceTag>
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
function processTitleGenerationJob(deps:
|
|
40
|
+
function processTitleGenerationJob(deps: TitleGenerationWorkerDeps, job: Job<TitleGenerationJob>): Promise<void> {
|
|
39
41
|
const { threadTitleService, recentActivityTitleService } = deps
|
|
40
42
|
if (job.data.kind === 'thread-title') {
|
|
41
43
|
return Effect.runPromise(
|
|
@@ -46,44 +48,53 @@ function processTitleGenerationJob(deps: TitleGenerationQueueDeps, job: Job<Titl
|
|
|
46
48
|
return Effect.runPromise(Effect.asVoid(recentActivityTitleService.refineRecentActivityTitle(job.data.activityId)))
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
concurrency: 10,
|
|
54
|
-
lockDuration: 300_000,
|
|
55
|
-
defaultJobOptions: { attempts: 3, backoff: { type: 'exponential', delay: 2_000 } },
|
|
56
|
-
prepare: ({ databaseService }) => databaseService.connect(),
|
|
57
|
-
processor: processTitleGenerationJob,
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
export function enqueueThreadTitleGeneration(job: Omit<ThreadTitleGenerationJob, 'kind'>) {
|
|
61
|
-
return titleGeneration.enqueue({ kind: 'thread-title', ...job }, { jobId: `thread-title:${job.threadId}` })
|
|
51
|
+
export interface TitleGenerationQueueRuntime {
|
|
52
|
+
enqueueThreadTitleGeneration(job: Omit<ThreadTitleGenerationJob, 'kind'>): Promise<void>
|
|
53
|
+
enqueueRecentActivityTitleRefinement(job: Omit<RecentActivityTitleRefinementJob, 'kind'>): Promise<void>
|
|
54
|
+
startWorker(options: { registerSignals?: boolean; deps: TitleGenerationWorkerDeps }): WorkerHandle
|
|
62
55
|
}
|
|
63
56
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
{ jobId: `recent-activity-title:${job.activityId}` },
|
|
68
|
-
)
|
|
57
|
+
interface MakeTitleGenerationQueueRuntimeParams {
|
|
58
|
+
connectionProvider: () => IORedis
|
|
59
|
+
queueJobService: QueueJobService
|
|
69
60
|
}
|
|
70
61
|
|
|
71
|
-
export function
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
62
|
+
export function makeTitleGenerationQueueRuntime(
|
|
63
|
+
params: MakeTitleGenerationQueueRuntimeParams,
|
|
64
|
+
): TitleGenerationQueueRuntime {
|
|
65
|
+
const { connectionProvider, queueJobService } = params
|
|
66
|
+
|
|
67
|
+
const queue = createQueueFactoryWithDeps<TitleGenerationJob, TitleGenerationWorkerDeps>({
|
|
68
|
+
name: TITLE_GENERATION_QUEUE,
|
|
69
|
+
displayName: 'Title generation',
|
|
70
|
+
jobName: 'title-generation',
|
|
71
|
+
concurrency: 10,
|
|
72
|
+
lockDuration: 300_000,
|
|
73
|
+
defaultJobOptions: { attempts: 3, backoff: { type: 'exponential', delay: 2_000 } },
|
|
74
|
+
connectionProvider,
|
|
75
|
+
queueJobService,
|
|
76
|
+
prepare: ({ databaseService }) => Effect.runPromise(databaseService.connect()),
|
|
77
|
+
processor: processTitleGenerationJob,
|
|
80
78
|
})
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
enqueueThreadTitleGeneration: (job) =>
|
|
82
|
+
queue.enqueue({ kind: 'thread-title', ...job }, { jobId: `thread-title:${job.threadId}` }),
|
|
83
|
+
enqueueRecentActivityTitleRefinement: (job) =>
|
|
84
|
+
queue.enqueue({ kind: 'recent-activity-title', ...job }, { jobId: `recent-activity-title:${job.activityId}` }),
|
|
85
|
+
startWorker: (options) =>
|
|
86
|
+
queue.startWorker({ deps: options.deps, registerSignals: options.registerSignals, connectionProvider }),
|
|
87
|
+
}
|
|
81
88
|
}
|
|
82
89
|
|
|
83
90
|
runStandaloneQueueWorker((runtime) => {
|
|
84
91
|
const resolve = <I, T>(tag: Context.Key<I, T>): T => runtime.runSync(Effect.service(tag))
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
const redis = resolve(RedisServiceTag)
|
|
93
|
+
const titleGenerationQueue = makeTitleGenerationQueueRuntime({
|
|
94
|
+
connectionProvider: () => redis.getConnectionForBullMQ(),
|
|
95
|
+
queueJobService: resolve(QueueJobServiceTag),
|
|
96
|
+
})
|
|
97
|
+
titleGenerationQueue.startWorker({
|
|
87
98
|
deps: {
|
|
88
99
|
databaseService: resolve(DatabaseServiceTag),
|
|
89
100
|
threadTitleService: resolve(ThreadTitleServiceTag),
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Schema, Effect } from 'effect'
|
|
2
|
-
import type IORedis from 'ioredis'
|
|
3
2
|
|
|
4
3
|
import { serverLogger } from '../config/logger'
|
|
5
4
|
import { LockAcquisitionError } from '../effect/errors'
|
|
6
5
|
import type { LockLostError, RedisError } from '../effect/errors'
|
|
6
|
+
import { RedisServiceTag } from '../effect/services'
|
|
7
|
+
import type { RedisConnectionManager } from './connection'
|
|
7
8
|
import { withLeaseLock } from './redis-lease-lock'
|
|
8
9
|
import type { RedisLeaseLockOptions } from './redis-lease-lock'
|
|
9
|
-
import { getRuntimeRedisConnection } from './runtime-connection'
|
|
10
10
|
|
|
11
11
|
const ORG_MEMORY_LOCK_PREFIX = 'lock:org-memory:org:'
|
|
12
12
|
const ORG_MEMORY_LOCK_TTL_MS = 120_000
|
|
@@ -20,9 +20,8 @@ class OrgMemoryLockCallbackError extends Schema.TaggedErrorClass<OrgMemoryLockCa
|
|
|
20
20
|
{ message: Schema.String, cause: Schema.Defect },
|
|
21
21
|
) {}
|
|
22
22
|
|
|
23
|
-
function createOrgMemoryLockOptions(
|
|
23
|
+
function createOrgMemoryLockOptions(orgId: string): RedisLeaseLockOptions {
|
|
24
24
|
return {
|
|
25
|
-
redis,
|
|
26
25
|
lockKey: `${ORG_MEMORY_LOCK_PREFIX}${orgId}`,
|
|
27
26
|
lockTtlMs: ORG_MEMORY_LOCK_TTL_MS,
|
|
28
27
|
retryDelayMs: ORG_MEMORY_LOCK_RETRY_DELAY_MS,
|
|
@@ -44,13 +43,28 @@ function createOrgMemoryLockOptions(redis: IORedis, orgId: string): RedisLeaseLo
|
|
|
44
43
|
}
|
|
45
44
|
}
|
|
46
45
|
|
|
47
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Promise-returning wrapper for hosts that live outside Effect.
|
|
48
|
+
*
|
|
49
|
+
* Callers must pass in a `RedisConnectionManager` (usually
|
|
50
|
+
* `runtime.redis.manager`) so the wrapper can build a standalone Redis layer
|
|
51
|
+
* instead of relying on an ambient managed runtime.
|
|
52
|
+
*/
|
|
53
|
+
export function withOrgMemoryLock<T>(
|
|
54
|
+
orgId: string,
|
|
55
|
+
fn: (signal: AbortSignal) => Promise<T>,
|
|
56
|
+
redisManager: RedisConnectionManager,
|
|
57
|
+
): Promise<T> {
|
|
48
58
|
return Effect.runPromise(
|
|
49
59
|
withOrgMemoryLockEffect(orgId, (signal) =>
|
|
50
60
|
Effect.tryPromise({
|
|
51
61
|
try: () => fn(signal),
|
|
52
62
|
catch: (cause) => new OrgMemoryLockCallbackError({ message: 'Org memory lock callback failed.', cause }),
|
|
53
63
|
}),
|
|
64
|
+
).pipe(
|
|
65
|
+
// Provide the redis service synchronously so the Effect has no residual
|
|
66
|
+
// requirements; the outer Promise call edge needs `R = never`.
|
|
67
|
+
Effect.provideService(RedisServiceTag, redisManager),
|
|
54
68
|
),
|
|
55
69
|
)
|
|
56
70
|
}
|
|
@@ -58,7 +72,7 @@ export function withOrgMemoryLock<T>(orgId: string, fn: (signal: AbortSignal) =>
|
|
|
58
72
|
export function withOrgMemoryLockEffect<A, E>(
|
|
59
73
|
orgId: string,
|
|
60
74
|
fn: (signal: AbortSignal) => Effect.Effect<A, E>,
|
|
61
|
-
): Effect.Effect<A, E | LockAcquisitionError | LockLostError | RedisError> {
|
|
75
|
+
): Effect.Effect<A, E | LockAcquisitionError | LockLostError | RedisError, RedisServiceTag> {
|
|
62
76
|
const normalizedOrgId = orgId.trim()
|
|
63
77
|
|
|
64
78
|
if (!normalizedOrgId) {
|
|
@@ -70,7 +84,8 @@ export function withOrgMemoryLockEffect<A, E>(
|
|
|
70
84
|
)
|
|
71
85
|
}
|
|
72
86
|
|
|
73
|
-
return Effect.
|
|
74
|
-
|
|
75
|
-
|
|
87
|
+
return Effect.gen(function* () {
|
|
88
|
+
const redis = yield* RedisServiceTag
|
|
89
|
+
return yield* withLeaseLock({ ...createOrgMemoryLockOptions(normalizedOrgId), redis: redis.getConnection() }, fn)
|
|
90
|
+
})
|
|
76
91
|
}
|
|
@@ -259,9 +259,16 @@ export function withLeaseLock<A, E, R>(
|
|
|
259
259
|
Effect.ignore,
|
|
260
260
|
)
|
|
261
261
|
|
|
262
|
+
// The refresh fiber races the user's effect against a `lockLost` Deferred
|
|
263
|
+
// so callers can observe Redis-side expirations. We pass the abort signal
|
|
264
|
+
// to `fn` so it can cooperatively wind down on lost-lock or external stop,
|
|
265
|
+
// but do NOT interrupt `fn` via `raceFirst` on `lockLost` — aborting a
|
|
266
|
+
// turn that has already produced tokens would discard the response. The
|
|
267
|
+
// caller owns the post-lease cleanup; the signal is enough for anyone who
|
|
268
|
+
// needs to bail out early.
|
|
262
269
|
return yield* Effect.scoped(
|
|
263
270
|
startRefreshFiber(redis, normalized, lockValue, abortController, lockLost).pipe(
|
|
264
|
-
Effect.andThen(fn(abortController.signal)
|
|
271
|
+
Effect.andThen(fn(abortController.signal)),
|
|
265
272
|
Effect.ensuring(cleanup),
|
|
266
273
|
),
|
|
267
274
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ResolvedAgentConfig } from '../config/agent-defaults'
|
|
2
2
|
import { asRecord, readOptionalString } from './thread-chat-helpers'
|
|
3
3
|
|
|
4
4
|
interface RuntimeAgentIdentityOverrides {
|
|
@@ -52,11 +52,15 @@ export function readRuntimeAgentIdentityOverrides(
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
export function resolveRuntimeAgentDisplayName(
|
|
55
|
+
export function resolveRuntimeAgentDisplayName(
|
|
56
|
+
agentConfig: ResolvedAgentConfig,
|
|
57
|
+
overrides: RuntimeAgentIdentityOverrides,
|
|
58
|
+
agentId: string,
|
|
59
|
+
): string {
|
|
56
60
|
const override = readStringOverride(overrides.displayNamesById, agentId)
|
|
57
61
|
if (override !== undefined) {
|
|
58
62
|
return override
|
|
59
63
|
}
|
|
60
64
|
|
|
61
|
-
return
|
|
65
|
+
return agentConfig.displayNames[agentId] ?? agentId
|
|
62
66
|
}
|
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
PlanNodeSpecRecord,
|
|
8
8
|
} from '@lota-sdk/shared'
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import type { ResolvedThreadBootstrapConfig } from '../config/thread-defaults'
|
|
11
11
|
import { resolveOnboardingOwnerAgentId } from '../config/thread-defaults'
|
|
12
12
|
export interface AgentRuntimeConfig<TAgent extends string> {
|
|
13
13
|
id: TAgent
|
|
@@ -162,13 +162,18 @@ export function buildThreadAgentToolPolicy<TAgent extends string, TSkill extends
|
|
|
162
162
|
linearInstalled: boolean
|
|
163
163
|
githubInstalled: boolean
|
|
164
164
|
provideRepoTool: boolean
|
|
165
|
-
leadAgentId
|
|
165
|
+
leadAgentId: TAgent
|
|
166
166
|
onboardingOwnerAgentId?: TAgent
|
|
167
|
+
threadBootstrapConfig?: ResolvedThreadBootstrapConfig
|
|
167
168
|
getAgentSkills: (agentId: TAgent, mode: ChatMode) => TSkill[]
|
|
168
169
|
}): AgentToolPolicy<TSkill> {
|
|
169
170
|
const resolvedMode = params.mode ?? toChatMode(params.threadType)
|
|
170
|
-
const leadAgentId = params.leadAgentId
|
|
171
|
-
const onboardingOwnerAgentId =
|
|
171
|
+
const leadAgentId = params.leadAgentId
|
|
172
|
+
const onboardingOwnerAgentId =
|
|
173
|
+
params.onboardingOwnerAgentId ??
|
|
174
|
+
(params.threadBootstrapConfig
|
|
175
|
+
? (resolveOnboardingOwnerAgentId(leadAgentId, params.threadBootstrapConfig) as TAgent)
|
|
176
|
+
: leadAgentId)
|
|
172
177
|
const skills = resolveActiveAgentSkills({
|
|
173
178
|
agentId: params.agentId,
|
|
174
179
|
threadType: params.threadType,
|
|
@@ -2,7 +2,7 @@ import type { ChatMessage } from '@lota-sdk/shared'
|
|
|
2
2
|
import type { LanguageModelUsage, UIMessageStreamOptions } from 'ai'
|
|
3
3
|
import { Duration, Effect, Exit, Scope } from 'effect'
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import type { ResolvedAgentConfig } from '../config/agent-defaults'
|
|
6
6
|
import { nowEpochMillis } from '../utils/date-time'
|
|
7
7
|
import { readRecord as _readRecord } from '../utils/string'
|
|
8
8
|
|
|
@@ -139,9 +139,14 @@ export function createTimedAbortSignal(parentSignal: AbortSignal, timeoutMs: num
|
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
export function buildSpecialistTaskMessage(params: {
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
export function buildSpecialistTaskMessage(params: {
|
|
143
|
+
agentConfig: ResolvedAgentConfig
|
|
144
|
+
agentId: string
|
|
145
|
+
task: string
|
|
146
|
+
}): ChatMessage {
|
|
147
|
+
const displayName = params.agentConfig.displayNames[params.agentId] ?? params.agentId
|
|
148
|
+
const leadAgentDisplayName =
|
|
149
|
+
params.agentConfig.displayNames[params.agentConfig.leadAgentId] ?? params.agentConfig.leadAgentId
|
|
145
150
|
return {
|
|
146
151
|
id: Bun.randomUUIDv7(),
|
|
147
152
|
role: 'user',
|
|
@@ -48,33 +48,31 @@ function tryContextCompactionPromise<A>(
|
|
|
48
48
|
})
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
function
|
|
52
|
-
return
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
).pipe(Effect.flatMap(parseCompactionOutput)),
|
|
70
|
-
)
|
|
51
|
+
function runContextCompacterEffect(helperModelRuntime: HelperModelRuntime, params: ContextCompactionRunnerParams) {
|
|
52
|
+
return tryContextCompactionPromise('Failed to compact runtime context.', () =>
|
|
53
|
+
helperModelRuntime.generateHelperStructured({
|
|
54
|
+
tag: 'context-compaction',
|
|
55
|
+
createAgent: createContextCompactionAgent,
|
|
56
|
+
messages: [
|
|
57
|
+
{
|
|
58
|
+
role: 'user',
|
|
59
|
+
content: buildContextCompactionPrompt({
|
|
60
|
+
previousSummary: params.previousSummary,
|
|
61
|
+
transcript: params.transcript,
|
|
62
|
+
}),
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
schema: ContextCompactionOutputSchema,
|
|
66
|
+
maxOutputTokens: 8_000,
|
|
67
|
+
}),
|
|
68
|
+
).pipe(Effect.flatMap(parseCompactionOutput))
|
|
71
69
|
}
|
|
72
70
|
|
|
73
71
|
export function createWiredContextCompactionRuntime(deps: CreateContextCompactionRuntimeDeps) {
|
|
74
72
|
const { helperModelRuntime } = deps
|
|
75
73
|
|
|
76
74
|
const runtime = createContextCompactionRuntime({
|
|
77
|
-
runCompacter: (params) =>
|
|
75
|
+
runCompacter: (params) => runContextCompacterEffect(helperModelRuntime, params),
|
|
78
76
|
now: deps.now,
|
|
79
77
|
randomId: deps.randomId,
|
|
80
78
|
thresholdRatio: CONTEXT_COMPACTION_THRESHOLD_RATIO,
|
|
@@ -86,20 +84,18 @@ export function createWiredContextCompactionRuntime(deps: CreateContextCompactio
|
|
|
86
84
|
includedToolPrefixes: CONTEXT_COMPACTION_INCLUDED_TOOL_PREFIXES,
|
|
87
85
|
})
|
|
88
86
|
|
|
89
|
-
function compactMemoryBlockSummary(params: { previousSummary: string; newEntriesText: string })
|
|
87
|
+
function compactMemoryBlockSummary(params: { previousSummary: string; newEntriesText: string }) {
|
|
90
88
|
const previousSummary = params.previousSummary.trim()
|
|
91
89
|
const newEntriesText = params.newEntriesText.trim()
|
|
92
|
-
if (!previousSummary && !newEntriesText) return
|
|
90
|
+
if (!previousSummary && !newEntriesText) return Effect.succeed('')
|
|
93
91
|
|
|
94
|
-
return
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}),
|
|
102
|
-
),
|
|
92
|
+
return tryContextCompactionPromise('Failed to compact memory block summary.', () =>
|
|
93
|
+
helperModelRuntime.generateHelperText({
|
|
94
|
+
tag: 'memory-block-compaction',
|
|
95
|
+
createAgent: createContextCompactionAgent,
|
|
96
|
+
messages: [{ role: 'user', content: buildMemoryBlockCompactionPrompt({ previousSummary, newEntriesText }) }],
|
|
97
|
+
maxOutputTokens: CONTEXT_COMPACTION_MAX_OUTPUT_TOKENS,
|
|
98
|
+
}),
|
|
103
99
|
)
|
|
104
100
|
}
|
|
105
101
|
|
|
@@ -88,7 +88,9 @@ export interface CompactionOutput {
|
|
|
88
88
|
summary: string
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
export type ContextCompactionRunner = (
|
|
91
|
+
export type ContextCompactionRunner = (
|
|
92
|
+
params: ContextCompactionRunnerParams,
|
|
93
|
+
) => Effect.Effect<CompactionOutput, unknown>
|
|
92
94
|
|
|
93
95
|
export interface CreateContextCompactionRuntimeOptions {
|
|
94
96
|
runCompacter: ContextCompactionRunner
|
|
@@ -112,7 +114,7 @@ export interface ContextCompactionRuntime {
|
|
|
112
114
|
liveMessages: ChatMessage[]
|
|
113
115
|
contextSize?: number
|
|
114
116
|
}) => CompactionAssessment
|
|
115
|
-
compactHistory: (params: CompactHistoryParams) =>
|
|
117
|
+
compactHistory: (params: CompactHistoryParams) => Effect.Effect<CompactHistoryResult, CompactionError>
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
function estimateTokens(text: string): number {
|
|
@@ -348,10 +350,10 @@ export function createContextCompactionRuntime(
|
|
|
348
350
|
Effect.gen(function* () {
|
|
349
351
|
const chunk = chunks[state.index]
|
|
350
352
|
const transcript = toCompactionTranscript(chunk)
|
|
351
|
-
const output = yield*
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
353
|
+
const output = yield* options.runCompacter({ previousSummary: state.summary, chunk, transcript }).pipe(
|
|
354
|
+
Effect.mapError((error) => new CompactionError({ message: String(error), cause: error })),
|
|
355
|
+
Effect.retry(COMPACTION_RUNNER_RETRY_OPTIONS),
|
|
356
|
+
)
|
|
355
357
|
return { summary: normalizeSummary(output.summary), index: state.index + 1 }
|
|
356
358
|
}),
|
|
357
359
|
},
|
|
@@ -479,7 +481,7 @@ export function createContextCompactionRuntime(
|
|
|
479
481
|
prependSummaryMessage,
|
|
480
482
|
estimateThreshold,
|
|
481
483
|
shouldCompactHistory,
|
|
482
|
-
compactHistory:
|
|
484
|
+
compactHistory: compactHistoryEffect,
|
|
483
485
|
}
|
|
484
486
|
}
|
|
485
487
|
|
|
@@ -13,6 +13,7 @@ import { Layer } from 'effect'
|
|
|
13
13
|
import { AiGatewayLive } from '../ai-gateway/ai-gateway'
|
|
14
14
|
import { EmbeddingCacheLive } from '../ai/embedding-cache'
|
|
15
15
|
import type { buildInfrastructureLayer } from '../effect/layers'
|
|
16
|
+
import { LotaQueuesLive } from '../queues/queues.service'
|
|
16
17
|
import { SharedThreadStreamSubscriberLive } from '../redis/stream-context'
|
|
17
18
|
import { AgentActivityServiceLive } from '../services/agent-activity.service'
|
|
18
19
|
import { AgentExecutorServiceLive } from '../services/agent-executor.service'
|
|
@@ -121,7 +122,6 @@ export function buildDomainServiceLayer(infrastructureLayer: InfrastructureLayer
|
|
|
121
122
|
OrganizationMemberServiceLive,
|
|
122
123
|
PlanApprovalServiceLive,
|
|
123
124
|
PlanRunServiceLive,
|
|
124
|
-
PlanSchedulerServiceLive,
|
|
125
125
|
PlanWorkspaceServiceLive,
|
|
126
126
|
PluginExecutorServiceLive,
|
|
127
127
|
QualityMetricsServiceLive,
|
|
@@ -135,7 +135,18 @@ export function buildDomainServiceLayer(infrastructureLayer: InfrastructureLayer
|
|
|
135
135
|
),
|
|
136
136
|
ctx0,
|
|
137
137
|
)
|
|
138
|
-
const
|
|
138
|
+
const ctx1Base = Layer.mergeAll(ctx0, tier1)
|
|
139
|
+
|
|
140
|
+
// LotaQueuesLive depends only on Redis + QueueJobService from tier1/infra.
|
|
141
|
+
// It's threaded in before the tiers that need queue enqueue access so the
|
|
142
|
+
// service layers that enqueue jobs (AutonomousJobService, plan scheduler,
|
|
143
|
+
// heartbeat, etc.) can resolve LotaQueuesServiceTag cleanly.
|
|
144
|
+
const queuesTier = provide(LotaQueuesLive, ctx1Base)
|
|
145
|
+
const ctx1 = Layer.mergeAll(ctx1Base, queuesTier)
|
|
146
|
+
// PlanSchedulerServiceLive previously sat in tier1; it now depends on the
|
|
147
|
+
// queue layer, so it moves to the merged ctx1 context with queues available.
|
|
148
|
+
const planSchedulerTier = provide(PlanSchedulerServiceLive, ctx1)
|
|
149
|
+
const ctx1WithScheduler = Layer.mergeAll(ctx1, planSchedulerTier)
|
|
139
150
|
|
|
140
151
|
const tier2 = provide(
|
|
141
152
|
Layer.mergeAll(
|
|
@@ -154,9 +165,9 @@ export function buildDomainServiceLayer(infrastructureLayer: InfrastructureLayer
|
|
|
154
165
|
SkillResolverServiceLive,
|
|
155
166
|
RecentActivityTitleServiceLive,
|
|
156
167
|
),
|
|
157
|
-
|
|
168
|
+
ctx1WithScheduler,
|
|
158
169
|
)
|
|
159
|
-
const ctx2 = Layer.mergeAll(
|
|
170
|
+
const ctx2 = Layer.mergeAll(ctx1WithScheduler, tier2)
|
|
160
171
|
|
|
161
172
|
const tier3 = provide(Layer.mergeAll(PlanValidatorServiceLive, ThreadServiceLive), ctx2)
|
|
162
173
|
const ctx3 = Layer.mergeAll(ctx2, tier3)
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { PlanExecutionVisibility, PlanNodeSpecRecord, PlanSpecRecord } from '@lota-sdk/shared'
|
|
2
2
|
|
|
3
|
+
import type { ResolvedAgentConfig } from '../config/agent-defaults'
|
|
3
4
|
import { isAgentName } from '../config/agent-defaults'
|
|
4
5
|
|
|
5
6
|
export function resolvePlanNodeExecutionVisibility(
|
|
7
|
+
agentConfig: ResolvedAgentConfig,
|
|
6
8
|
plan: Pick<PlanSpecRecord, 'defaultExecutionVisibility'>,
|
|
7
9
|
node: Pick<PlanNodeSpecRecord, 'executionVisibility' | 'owner'>,
|
|
8
10
|
): PlanExecutionVisibility {
|
|
@@ -12,12 +14,13 @@ export function resolvePlanNodeExecutionVisibility(
|
|
|
12
14
|
return configuredVisibility
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
return node.owner.executorType === 'agent' && isAgentName(node.owner.ref) ? 'visible' : 'silent'
|
|
17
|
+
return node.owner.executorType === 'agent' && isAgentName(agentConfig, node.owner.ref) ? 'visible' : 'silent'
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export function shouldPlanNodeUseVisibleTurn(
|
|
21
|
+
agentConfig: ResolvedAgentConfig,
|
|
19
22
|
plan: Pick<PlanSpecRecord, 'defaultExecutionVisibility'>,
|
|
20
23
|
node: Pick<PlanNodeSpecRecord, 'executionVisibility' | 'owner'>,
|
|
21
24
|
): boolean {
|
|
22
|
-
return resolvePlanNodeExecutionVisibility(plan, node) === 'visible'
|
|
25
|
+
return resolvePlanNodeExecutionVisibility(agentConfig, plan, node) === 'visible'
|
|
23
26
|
}
|
|
@@ -1,27 +1,5 @@
|
|
|
1
1
|
import type { GraphDesignRequest, GraphDesignResponse } from '@lota-sdk/shared'
|
|
2
2
|
|
|
3
|
-
import { resolveOptionalLotaService } from '../effect/runtime'
|
|
4
|
-
import { RuntimeConfigServiceTag } from '../effect/services'
|
|
5
|
-
|
|
6
3
|
export interface GraphDesigner {
|
|
7
4
|
designGraph(request: GraphDesignRequest): Promise<GraphDesignResponse>
|
|
8
5
|
}
|
|
9
|
-
|
|
10
|
-
let currentGraphDesigner: GraphDesigner | null = null
|
|
11
|
-
|
|
12
|
-
export function configureGraphDesigner(graphDesigner: GraphDesigner | null | undefined): void {
|
|
13
|
-
currentGraphDesigner = graphDesigner ?? null
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function clearGraphDesigner(): void {
|
|
17
|
-
currentGraphDesigner = null
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function getGraphDesigner(): GraphDesigner | null {
|
|
21
|
-
if (currentGraphDesigner) {
|
|
22
|
-
return currentGraphDesigner
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const runtimeConfig = resolveOptionalLotaService(RuntimeConfigServiceTag)
|
|
26
|
-
return runtimeConfig?.graphDesigner ?? null
|
|
27
|
-
}
|
package/src/runtime/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './approval-continuation'
|
|
|
2
2
|
export * from './agent-runtime-policy'
|
|
3
3
|
export * from './agent-stream-helpers'
|
|
4
4
|
export * from './chat-request-routing'
|
|
5
|
+
export * from './chat-run-orchestration'
|
|
5
6
|
export * from './chat-run-registry'
|
|
6
7
|
export * from './context-compaction/context-compaction'
|
|
7
8
|
export * from './execution-plan'
|
|
@@ -12,6 +13,7 @@ export * from './instruction-sections'
|
|
|
12
13
|
export * from './memory/memory-block'
|
|
13
14
|
export * from './memory/memory-digest-policy'
|
|
14
15
|
export * from './memory/memory-scope'
|
|
16
|
+
export * from './live-turn-trace'
|
|
15
17
|
export * from './llm-content'
|
|
16
18
|
export * from './plugin-resolution'
|
|
17
19
|
export * from './plugin-types'
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { getAgentRoster } from '../config/agent-defaults'
|
|
2
|
-
|
|
3
1
|
export type IndexedRepoAgentName = string
|
|
4
2
|
|
|
5
3
|
export const REPO_SECTION_NAMES = [
|
|
@@ -16,13 +14,11 @@ export type RepoSectionName = (typeof REPO_SECTION_NAMES)[number]
|
|
|
16
14
|
const ALL_REPO_SECTIONS: RepoSectionName[] = [...REPO_SECTION_NAMES]
|
|
17
15
|
|
|
18
16
|
export function buildDefaultRepoSectionsByAgent(
|
|
19
|
-
roster: readonly IndexedRepoAgentName[]
|
|
17
|
+
roster: readonly IndexedRepoAgentName[],
|
|
20
18
|
): Record<IndexedRepoAgentName, RepoSectionName[]> {
|
|
21
19
|
return Object.fromEntries(roster.map((agentId) => [agentId, [...ALL_REPO_SECTIONS]]))
|
|
22
20
|
}
|
|
23
21
|
|
|
24
|
-
export function emptyAgentContextMap(
|
|
25
|
-
roster: readonly IndexedRepoAgentName[] = getAgentRoster(),
|
|
26
|
-
): Record<IndexedRepoAgentName, string> {
|
|
22
|
+
export function emptyAgentContextMap(roster: readonly IndexedRepoAgentName[]): Record<IndexedRepoAgentName, string> {
|
|
27
23
|
return Object.fromEntries(roster.map((agentId) => [agentId, '']))
|
|
28
24
|
}
|