@lota-sdk/core 0.4.12 → 0.4.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/package.json +4 -4
  2. package/src/ai/embedding-cache.ts +17 -11
  3. package/src/ai-gateway/ai-gateway.ts +164 -94
  4. package/src/ai-gateway/index.ts +4 -1
  5. package/src/config/agent-defaults.ts +2 -2
  6. package/src/config/agent-types.ts +1 -1
  7. package/src/create-runtime.ts +259 -200
  8. package/src/db/cursor-pagination.ts +2 -9
  9. package/src/db/memory-store.ts +194 -175
  10. package/src/db/memory.ts +125 -71
  11. package/src/db/schema-fingerprint.ts +5 -4
  12. package/src/db/service-normalization.ts +4 -3
  13. package/src/db/service.ts +3 -2
  14. package/src/db/startup.ts +15 -16
  15. package/src/effect/errors.ts +161 -21
  16. package/src/effect/index.ts +0 -1
  17. package/src/embeddings/provider.ts +15 -7
  18. package/src/queues/autonomous-job.queue.ts +10 -22
  19. package/src/queues/delayed-node-promotion.queue.ts +8 -14
  20. package/src/queues/document-processor.queue.ts +13 -4
  21. package/src/queues/memory-consolidation.queue.ts +26 -14
  22. package/src/queues/plan-agent-heartbeat.queue.ts +10 -9
  23. package/src/queues/plan-scheduler.queue.ts +37 -15
  24. package/src/queues/queue-factory.ts +59 -35
  25. package/src/queues/standalone-worker.ts +3 -2
  26. package/src/redis/connection.ts +10 -3
  27. package/src/redis/org-memory-lock.ts +1 -1
  28. package/src/redis/redis-lease-lock.ts +5 -5
  29. package/src/redis/stream-context.ts +1 -1
  30. package/src/runtime/chat-message.ts +64 -1
  31. package/src/runtime/chat-run-orchestration.ts +33 -20
  32. package/src/runtime/context-compaction/context-compaction-runtime.ts +14 -7
  33. package/src/runtime/context-compaction/context-compaction.ts +78 -66
  34. package/src/runtime/domain-layer.ts +13 -7
  35. package/src/runtime/execution-plan.ts +7 -3
  36. package/src/runtime/live-turn-trace.ts +6 -49
  37. package/src/runtime/memory/memory-block.ts +3 -9
  38. package/src/runtime/memory/memory-scope.ts +3 -1
  39. package/src/runtime/plugin-resolution.ts +2 -1
  40. package/src/runtime/post-turn-side-effects.ts +6 -5
  41. package/src/runtime/retrieval-adapters.ts +8 -20
  42. package/src/runtime/runtime-config.ts +3 -9
  43. package/src/runtime/runtime-extensions.ts +2 -4
  44. package/src/runtime/runtime-lifecycle.ts +56 -16
  45. package/src/runtime/runtime-services.ts +180 -102
  46. package/src/runtime/runtime-worker-registry.ts +3 -1
  47. package/src/runtime/social-chat/social-chat-agent-runner.ts +1 -1
  48. package/src/runtime/social-chat/social-chat-history.ts +21 -18
  49. package/src/runtime/social-chat/social-chat.ts +356 -223
  50. package/src/runtime/specialist-runner.ts +3 -1
  51. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +3 -2
  52. package/src/runtime/thread-turn-context.ts +142 -102
  53. package/src/runtime/turn-lifecycle.ts +15 -46
  54. package/src/services/agent-activity.service.ts +1 -1
  55. package/src/services/agent-executor.service.ts +107 -77
  56. package/src/services/autonomous-job.service.ts +354 -293
  57. package/src/services/background-work.service.ts +3 -3
  58. package/src/services/context-compaction.service.ts +7 -2
  59. package/src/services/document-chunk.service.ts +50 -32
  60. package/src/services/execution-plan/execution-plan-schedule.ts +5 -3
  61. package/src/services/execution-plan/execution-plan.service.ts +162 -179
  62. package/src/services/feedback-loop.service.ts +5 -4
  63. package/src/services/graph-full-routing.ts +37 -36
  64. package/src/services/institutional-memory.service.ts +28 -30
  65. package/src/services/learned-skill.service.ts +107 -72
  66. package/src/services/memory/memory-errors.ts +4 -23
  67. package/src/services/memory/memory-org-memory.ts +10 -5
  68. package/src/services/memory/memory-rerank.ts +18 -6
  69. package/src/services/memory/memory.service.ts +170 -111
  70. package/src/services/memory/rerank.service.ts +29 -20
  71. package/src/services/organization-member.service.ts +1 -1
  72. package/src/services/organization.service.ts +69 -75
  73. package/src/services/ownership-dispatcher.service.ts +40 -39
  74. package/src/services/plan/plan-agent-heartbeat.service.ts +26 -23
  75. package/src/services/plan/plan-agent-query.service.ts +39 -31
  76. package/src/services/plan/plan-completion-side-effects.ts +13 -17
  77. package/src/services/plan/plan-coordination.service.ts +2 -1
  78. package/src/services/plan/plan-cycle.service.ts +6 -5
  79. package/src/services/plan/plan-deadline.service.ts +57 -54
  80. package/src/services/plan/plan-event-delivery.service.ts +5 -4
  81. package/src/services/plan/plan-executor-graph.ts +18 -15
  82. package/src/services/plan/plan-executor.service.ts +235 -262
  83. package/src/services/plan/plan-run.service.ts +169 -93
  84. package/src/services/plan/plan-scheduler.service.ts +192 -202
  85. package/src/services/plan/plan-template.service.ts +1 -1
  86. package/src/services/plan/plan-transaction-events.ts +1 -1
  87. package/src/services/plan/plan-workspace.service.ts +23 -14
  88. package/src/services/plugin-executor.service.ts +5 -9
  89. package/src/services/queue-job.service.ts +117 -59
  90. package/src/services/recent-activity-title.service.ts +13 -12
  91. package/src/services/recent-activity.service.ts +6 -1
  92. package/src/services/social-chat-history.service.ts +29 -25
  93. package/src/services/system-executor.service.ts +5 -9
  94. package/src/services/thread/thread-active-run.ts +2 -2
  95. package/src/services/thread/thread-listing.ts +61 -57
  96. package/src/services/thread/thread-memory-block.ts +73 -48
  97. package/src/services/thread/thread-message.service.ts +76 -65
  98. package/src/services/thread/thread-record-store.ts +8 -8
  99. package/src/services/thread/thread-title.service.ts +10 -4
  100. package/src/services/thread/thread-turn-execution.ts +43 -45
  101. package/src/services/thread/thread-turn-preparation.service.ts +257 -135
  102. package/src/services/thread/thread-turn-streaming.ts +82 -85
  103. package/src/services/thread/thread-turn.ts +8 -8
  104. package/src/services/thread/thread.service.ts +135 -100
  105. package/src/services/user.service.ts +45 -48
  106. package/src/storage/attachment-parser.ts +6 -2
  107. package/src/storage/attachment-storage.service.ts +5 -6
  108. package/src/storage/generated-document-storage.service.ts +1 -1
  109. package/src/system-agents/context-compaction.agent.ts +10 -9
  110. package/src/system-agents/delegated-agent-factory.ts +30 -6
  111. package/src/system-agents/memory-reranker.agent.ts +10 -9
  112. package/src/system-agents/memory.agent.ts +10 -9
  113. package/src/system-agents/recent-activity-title-refiner.agent.ts +13 -15
  114. package/src/system-agents/regular-chat-memory-digest.agent.ts +13 -12
  115. package/src/system-agents/skill-extractor.agent.ts +13 -12
  116. package/src/system-agents/skill-manager.agent.ts +13 -12
  117. package/src/system-agents/thread-router.agent.ts +10 -5
  118. package/src/system-agents/title-generator.agent.ts +13 -12
  119. package/src/tools/fetch-webpage.tool.ts +13 -13
  120. package/src/tools/memory-block.tool.ts +3 -1
  121. package/src/tools/plan-approval.tool.ts +4 -2
  122. package/src/tools/read-file-parts.tool.ts +10 -4
  123. package/src/tools/remember-memory.tool.ts +3 -1
  124. package/src/tools/research-topic.tool.ts +9 -5
  125. package/src/tools/search-web.tool.ts +16 -16
  126. package/src/tools/search.tool.ts +20 -5
  127. package/src/tools/team-think.tool.ts +61 -38
  128. package/src/utils/async.ts +5 -5
  129. package/src/utils/errors.ts +19 -18
  130. package/src/utils/sse-keepalive.ts +28 -25
  131. package/src/workers/bootstrap.ts +75 -11
  132. package/src/workers/memory-consolidation.worker.ts +82 -91
  133. package/src/workers/organization-learning.worker.ts +14 -4
  134. package/src/workers/regular-chat-memory-digest.runner.ts +105 -67
  135. package/src/workers/skill-extraction.runner.ts +97 -61
  136. package/src/workers/utils/repo-structure-extractor.ts +13 -8
  137. package/src/workers/utils/thread-message-query.ts +24 -24
  138. package/src/workers/worker-utils.ts +23 -4
  139. package/src/effect/helpers.ts +0 -123
@@ -6,7 +6,7 @@ import { ensureRecordId, recordIdToString } from '../db/record-id'
6
6
  import type { RecordIdInput } from '../db/record-id'
7
7
  import type { SurrealDBService } from '../db/service'
8
8
  import { TABLES } from '../db/tables'
9
- import { NotFoundError } from '../effect/errors'
9
+ import { ERROR_TAGS, NotFoundError } from '../effect/errors'
10
10
  import { DatabaseServiceTag } from '../effect/services'
11
11
  import { toIsoDateTimeString } from '../utils/date-time'
12
12
 
@@ -28,7 +28,7 @@ function userNotFoundError(userId: RecordIdInput): NotFoundError {
28
28
  })
29
29
  }
30
30
 
31
- class UserServiceError extends Schema.TaggedErrorClass<UserServiceError>()('UserServiceError', {
31
+ class UserServiceError extends Schema.TaggedErrorClass<UserServiceError>()(ERROR_TAGS.UserServiceError, {
32
32
  operation: Schema.String,
33
33
  cause: Schema.Defect,
34
34
  }) {}
@@ -38,59 +38,56 @@ function toUserServiceError(operation: string, cause: unknown): UserServiceError
38
38
  }
39
39
 
40
40
  export function makeUserService(db: SurrealDBService) {
41
- function upsertUserEffect(params: { id: RecordIdInput; name: string; email: string }) {
41
+ const upsertUser = Effect.fn('UserService.upsertUser')(function* (params: {
42
+ id: RecordIdInput
43
+ name: string
44
+ email: string
45
+ }) {
42
46
  const userRef = ensureRecordId(params.id, TABLES.USER)
43
- return db.upsert(TABLES.USER, userRef, { name: params.name, email: params.email }, sdkUserRecordSchema).pipe(
44
- Effect.mapError((cause) => toUserServiceError('upsertUser', cause)),
45
- Effect.map(toPublic),
46
- )
47
- }
47
+ const record = yield* db
48
+ .upsert(TABLES.USER, userRef, { name: params.name, email: params.email }, sdkUserRecordSchema)
49
+ .pipe(Effect.mapError((cause) => toUserServiceError('upsertUser', cause)))
50
+ return toPublic(record)
51
+ })
48
52
 
49
- function getUserEffect(userId: RecordIdInput) {
50
- return db.findOne(TABLES.USER, { id: ensureRecordId(userId, TABLES.USER) }, sdkUserRecordSchema).pipe(
51
- Effect.mapError((cause) => toUserServiceError('getUser', cause)),
52
- Effect.flatMap((record) => (record ? Effect.succeed(record) : Effect.fail(userNotFoundError(userId)))),
53
- Effect.map(toPublic),
54
- )
55
- }
53
+ const getUser = Effect.fn('UserService.getUser')(function* (userId: RecordIdInput) {
54
+ const record = yield* db
55
+ .findOne(TABLES.USER, { id: ensureRecordId(userId, TABLES.USER) }, sdkUserRecordSchema)
56
+ .pipe(Effect.mapError((cause) => toUserServiceError('getUser', cause)))
57
+ if (!record) return yield* userNotFoundError(userId)
58
+ return toPublic(record)
59
+ })
56
60
 
57
- function listUsersEffect() {
58
- return db.findMany(TABLES.USER, {}, sdkUserRecordSchema, { orderBy: 'createdAt', orderDir: 'ASC' }).pipe(
59
- Effect.mapError((cause) => toUserServiceError('listUsers', cause)),
60
- Effect.map((records) => records.map(toPublic)),
61
- )
62
- }
61
+ const listUsers = Effect.fn('UserService.listUsers')(function* () {
62
+ const records = yield* db
63
+ .findMany(TABLES.USER, {}, sdkUserRecordSchema, { orderBy: 'createdAt', orderDir: 'ASC' })
64
+ .pipe(Effect.mapError((cause) => toUserServiceError('listUsers', cause)))
65
+ return records.map(toPublic)
66
+ })
63
67
 
64
- function updateUserEffect(userId: RecordIdInput, params: { name?: string; email?: string }) {
65
- return db.update(TABLES.USER, ensureRecordId(userId, TABLES.USER), params, sdkUserRecordSchema).pipe(
66
- Effect.mapError((cause) => toUserServiceError('updateUser', cause)),
67
- Effect.flatMap((updated) => (updated ? Effect.succeed(updated) : Effect.fail(userNotFoundError(userId)))),
68
- Effect.map(toPublic),
69
- )
70
- }
68
+ const updateUser = Effect.fn('UserService.updateUser')(function* (
69
+ userId: RecordIdInput,
70
+ params: { name?: string; email?: string },
71
+ ) {
72
+ const updated = yield* db
73
+ .update(TABLES.USER, ensureRecordId(userId, TABLES.USER), params, sdkUserRecordSchema)
74
+ .pipe(Effect.mapError((cause) => toUserServiceError('updateUser', cause)))
75
+ if (!updated) return yield* userNotFoundError(userId)
76
+ return toPublic(updated)
77
+ })
71
78
 
72
- function deleteUserEffect(userId: RecordIdInput) {
79
+ const deleteUser = Effect.fn('UserService.deleteUser')(function* (userId: RecordIdInput) {
73
80
  const userRef = ensureRecordId(userId, TABLES.USER)
74
- return Effect.gen(function* () {
75
- yield* db
76
- .deleteWhere(TABLES.ORGANIZATION_MEMBER, { in: userRef })
77
- .pipe(Effect.mapError((cause) => toUserServiceError('deleteUser.deleteMemberships', cause)))
78
- const deleted = yield* db
79
- .deleteById(TABLES.USER, userRef)
80
- .pipe(Effect.mapError((cause) => toUserServiceError('deleteUser.deleteUser', cause)))
81
- if (!deleted) {
82
- return yield* userNotFoundError(userId)
83
- }
84
- })
85
- }
81
+ yield* db
82
+ .deleteWhere(TABLES.ORGANIZATION_MEMBER, { in: userRef })
83
+ .pipe(Effect.mapError((cause) => toUserServiceError('deleteUser.deleteMemberships', cause)))
84
+ const deleted = yield* db
85
+ .deleteById(TABLES.USER, userRef)
86
+ .pipe(Effect.mapError((cause) => toUserServiceError('deleteUser.deleteUser', cause)))
87
+ if (!deleted) return yield* userNotFoundError(userId)
88
+ })
86
89
 
87
- return {
88
- upsertUser: upsertUserEffect,
89
- getUser: getUserEffect,
90
- listUsers: listUsersEffect,
91
- updateUser: updateUserEffect,
92
- deleteUser: deleteUserEffect,
93
- }
90
+ return { upsertUser, getUser, listUsers, updateUser, deleteUser }
94
91
  }
95
92
 
96
93
  export class UserServiceTag extends Context.Service<UserServiceTag, ReturnType<typeof makeUserService>>()(
@@ -3,9 +3,11 @@ import { Schema, Effect } from 'effect'
3
3
  import mammoth from 'mammoth'
4
4
  import { PDFParse } from 'pdf-parse'
5
5
 
6
+ import { ERROR_TAGS } from '../effect/errors'
7
+
6
8
  const READ_FILE_PARTS_CHARS_PER_PAGE = 3500
7
9
 
8
- class AttachmentParserError extends Schema.TaggedErrorClass<AttachmentParserError>()('AttachmentParserError', {
10
+ class AttachmentParserError extends Schema.TaggedErrorClass<AttachmentParserError>()(ERROR_TAGS.AttachmentParserError, {
9
11
  message: Schema.String,
10
12
  cause: Schema.Defect,
11
13
  }) {}
@@ -67,7 +69,9 @@ export function extractPdfPages(file: File): Promise<string[]> {
67
69
  }).pipe(
68
70
  Effect.ensuring(
69
71
  tryAttachmentPromise('Failed to destroy PDF parser.', () => parser.destroy()).pipe(
70
- Effect.orDie,
72
+ Effect.catchTag(ERROR_TAGS.AttachmentParserError, (error) =>
73
+ Effect.logWarning(`Attachment parser cleanup failed: ${error.message}`),
74
+ ),
71
75
  Effect.asVoid,
72
76
  ),
73
77
  ),
@@ -3,7 +3,7 @@ import { S3Client } from 'bun'
3
3
  import { Context, Schema, Effect, Layer } from 'effect'
4
4
 
5
5
  import { serverLogger } from '../config/logger'
6
- import { BadRequestError, ForbiddenError } from '../effect/errors'
6
+ import { ERROR_TAGS, BadRequestError, ForbiddenError } from '../effect/errors'
7
7
  import { RuntimeConfigServiceTag } from '../effect/services'
8
8
  import type { ResolvedLotaRuntimeConfig } from '../runtime/runtime-config'
9
9
  import { getErrorMessage } from '../utils/errors'
@@ -36,11 +36,10 @@ export type UploadedThreadAttachment = {
36
36
 
37
37
  type AttachmentContextCandidate = { storageKey: string; name: string; contentType: string; sizeBytes: number | null }
38
38
 
39
- class AttachmentStorageError extends Schema.TaggedErrorClass<AttachmentStorageError>()('AttachmentStorageError', {
40
- operation: Schema.String,
41
- message: Schema.String,
42
- cause: Schema.Defect,
43
- }) {}
39
+ class AttachmentStorageError extends Schema.TaggedErrorClass<AttachmentStorageError>()(
40
+ ERROR_TAGS.AttachmentStorageError,
41
+ { operation: Schema.String, message: Schema.String, cause: Schema.Defect },
42
+ ) {}
44
43
 
45
44
  function toAttachmentStorageError(operation: string, cause: unknown): AttachmentStorageError {
46
45
  return new AttachmentStorageError({ operation, message: getErrorMessage(cause), cause })
@@ -26,7 +26,7 @@ function buildGeneratedDocumentStorageKey(params: {
26
26
  }
27
27
 
28
28
  class GeneratedDocumentStorageError extends Schema.TaggedErrorClass<GeneratedDocumentStorageError>()(
29
- 'GeneratedDocumentStorageError',
29
+ '@lota-sdk/core/GeneratedDocumentStorageError',
30
30
  { message: Schema.String, cause: Schema.Defect },
31
31
  ) {}
32
32
 
@@ -1,7 +1,7 @@
1
1
  import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
2
2
  import { ToolLoopAgent } from 'ai'
3
3
 
4
- import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
4
+ import type { AiGatewayModels } from '../ai-gateway/ai-gateway'
5
5
  import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
6
6
  import {
7
7
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
@@ -31,12 +31,13 @@ The caller enforces a structured output schema with exactly one field:
31
31
  </output-format>
32
32
  </agent-instructions>`
33
33
 
34
- export function createContextCompactionAgent(options: CreateHelperToolLoopAgentOptions) {
35
- return new ToolLoopAgent({
36
- id: 'context-compaction',
37
- model: aiGatewayChatModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
38
- headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
39
- providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
40
- ...resolveHelperAgentOptions(options, { instructions: CONTEXT_COMPACTION_PROMPT }),
41
- })
34
+ export function makeContextCompactionAgentFactory(models: AiGatewayModels) {
35
+ return (options: CreateHelperToolLoopAgentOptions) =>
36
+ new ToolLoopAgent({
37
+ id: 'context-compaction',
38
+ model: models.chatModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
39
+ headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
40
+ providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
41
+ ...resolveHelperAgentOptions(options, { instructions: CONTEXT_COMPACTION_PROMPT }),
42
+ })
42
43
  }
@@ -5,6 +5,7 @@ import { z } from 'zod'
5
5
 
6
6
  import type { ToolDefinition } from '../ai/definitions'
7
7
  import { aiLogger } from '../config/logger'
8
+ import { ERROR_TAGS } from '../effect/errors'
8
9
  import { formatUtcPromptDate, nowDate } from '../utils/date-time'
9
10
  import { getErrorMessage } from '../utils/errors'
10
11
  import { isRecord } from '../utils/string'
@@ -12,6 +13,7 @@ import { assertSubstantiveAgentResult } from './agent-result'
12
13
 
13
14
  type AgentProviderOptions = ToolLoopAgentSettings['providerOptions']
14
15
  type AgentModel = LanguageModel | (() => LanguageModel)
16
+ type AgentModelWithContext<TContext> = LanguageModel | ((context: TContext) => LanguageModel)
15
17
 
16
18
  interface DelegatedAgentDefinition {
17
19
  id: string
@@ -24,13 +26,26 @@ interface DelegatedAgentDefinition {
24
26
  maxSteps?: number
25
27
  maxOutputTokens?: number
26
28
  temperature?: number
29
+ runPromise: <A, E>(effect: Effect.Effect<A, E, never>, options?: { signal?: AbortSignal }) => Promise<A>
27
30
  }
28
31
 
29
- interface DelegatedAgentDefinitionWithContext<TContext> extends Omit<DelegatedAgentDefinition, 'tools'> {
32
+ type DelegatedAgentRunPromise = <A, E>(
33
+ effect: Effect.Effect<A, E, never>,
34
+ options?: { signal?: AbortSignal },
35
+ ) => Promise<A>
36
+
37
+ interface DelegatedAgentDefinitionWithContext<TContext> extends Omit<
38
+ DelegatedAgentDefinition,
39
+ 'tools' | 'model' | 'runPromise'
40
+ > {
41
+ model: AgentModelWithContext<TContext>
30
42
  createTools: (context: TContext) => ToolSet
43
+ /** Pull runPromise off the caller's context so each tool invocation carries
44
+ * its own runtime binding rather than closing over a boot-time one. */
45
+ getRunPromise: (context: TContext) => DelegatedAgentRunPromise
31
46
  }
32
47
 
33
- class DelegatedAgentError extends Schema.TaggedErrorClass<DelegatedAgentError>()('DelegatedAgentError', {
48
+ class DelegatedAgentError extends Schema.TaggedErrorClass<DelegatedAgentError>()(ERROR_TAGS.DelegatedAgentError, {
34
49
  stage: Schema.Literals(['generate', 'validate', 'follow-up-generate', 'follow-up-validate']),
35
50
  label: Schema.String,
36
51
  message: Schema.String,
@@ -49,6 +64,13 @@ function resolveAgentModel(model: AgentModel): LanguageModel {
49
64
  return typeof model === 'function' ? model() : model
50
65
  }
51
66
 
67
+ function resolveAgentModelWithContext<TContext>(
68
+ model: AgentModelWithContext<TContext>,
69
+ context: TContext,
70
+ ): LanguageModel {
71
+ return typeof model === 'function' ? (model as (ctx: TContext) => LanguageModel)(context) : model
72
+ }
73
+
52
74
  function buildCurrentDateContext(now = nowDate()): string {
53
75
  return [`Today is ${formatUtcPromptDate(now)}.`, 'Use this exact date for any recency reasoning.'].join(' ')
54
76
  }
@@ -229,7 +251,7 @@ export function createDelegatedAgentTool(definition: DelegatedAgentDefinition):
229
251
  description: definition.description,
230
252
  inputSchema: z.object({ task: z.string().min(1) }),
231
253
  execute: ({ task }: { task: string }, { abortSignal }) =>
232
- Effect.runPromise(
254
+ definition.runPromise(
233
255
  Effect.gen(function* () {
234
256
  const agentTools = definition.tools
235
257
  const createAgent = () =>
@@ -268,6 +290,7 @@ export function createDelegatedAgentTool(definition: DelegatedAgentDefinition):
268
290
  }),
269
291
  }
270
292
  }),
293
+ abortSignal ? { signal: abortSignal } : undefined,
271
294
  ),
272
295
  }),
273
296
  } as const satisfies ToolDefinition<void>
@@ -286,13 +309,13 @@ export function createDelegatedAgentToolWithContext<TContext>(
286
309
  description: definition.description,
287
310
  inputSchema: z.object({ task: z.string().min(1) }),
288
311
  execute: ({ task }: { task: string }, { abortSignal }) =>
289
- Effect.runPromise(
312
+ definition.getRunPromise(context)(
290
313
  Effect.gen(function* () {
291
314
  const agentTools = definition.createTools(context)
292
315
  const createAgent = () =>
293
316
  new ToolLoopAgent({
294
317
  id: definition.id,
295
- model: resolveAgentModel(definition.model),
318
+ model: resolveAgentModelWithContext(definition.model, context),
296
319
  ...(definition.providerOptions ? { providerOptions: definition.providerOptions } : {}),
297
320
  ...(definition.headers ? { headers: definition.headers } : {}),
298
321
  instructions: buildDelegatedAgentInstructions(definition.instructions, agentTools),
@@ -304,7 +327,7 @@ export function createDelegatedAgentToolWithContext<TContext>(
304
327
  })
305
328
  const synthesize = (prompt: string, abortSignal?: AbortSignal) =>
306
329
  generateText({
307
- model: resolveAgentModel(definition.model),
330
+ model: resolveAgentModelWithContext(definition.model, context),
308
331
  ...(definition.providerOptions ? { providerOptions: definition.providerOptions } : {}),
309
332
  ...(definition.headers ? { headers: definition.headers } : {}),
310
333
  system: `${definition.instructions.trim()}\n\nReturn a complete final markdown answer using only the provided research data. Do not call tools.`,
@@ -325,6 +348,7 @@ export function createDelegatedAgentToolWithContext<TContext>(
325
348
  }),
326
349
  }
327
350
  }),
351
+ abortSignal ? { signal: abortSignal } : undefined,
328
352
  ),
329
353
  }),
330
354
  } as const satisfies ToolDefinition<TContext>
@@ -1,7 +1,7 @@
1
1
  import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
2
2
  import { ToolLoopAgent } from 'ai'
3
3
 
4
- import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
4
+ import type { AiGatewayModels } from '../ai-gateway/ai-gateway'
5
5
  import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
6
6
  import {
7
7
  OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS,
@@ -29,12 +29,13 @@ Set every item.relevance as a string; use empty string when no reason is needed.
29
29
  </output>
30
30
  </agent-instructions>`
31
31
 
32
- export function createMemoryRerankerAgent(options: CreateHelperToolLoopAgentOptions) {
33
- return new ToolLoopAgent({
34
- id: 'memory-reranker',
35
- model: aiGatewayChatModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
36
- headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
37
- providerOptions: OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS,
38
- ...resolveHelperAgentOptions(options),
39
- })
32
+ export function makeMemoryRerankerAgentFactory(models: AiGatewayModels) {
33
+ return (options: CreateHelperToolLoopAgentOptions) =>
34
+ new ToolLoopAgent({
35
+ id: 'memory-reranker',
36
+ model: models.chatModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
37
+ headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
38
+ providerOptions: OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS,
39
+ ...resolveHelperAgentOptions(options),
40
+ })
40
41
  }
@@ -1,7 +1,7 @@
1
1
  import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
2
2
  import { ToolLoopAgent } from 'ai'
3
3
 
4
- import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
4
+ import type { AiGatewayModels } from '../ai-gateway/ai-gateway'
5
5
  import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
6
6
  import {
7
7
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
@@ -49,12 +49,13 @@ Return only the schema fields with no extra formatting.
49
49
  </format>
50
50
  </agent-instructions>`
51
51
 
52
- export function createOrgMemoryAgent(options: CreateHelperToolLoopAgentOptions) {
53
- return new ToolLoopAgent({
54
- id: 'org-memory',
55
- model: aiGatewayChatModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
56
- headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
57
- providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
58
- ...resolveHelperAgentOptions(options),
59
- })
52
+ export function makeOrgMemoryAgentFactory(models: AiGatewayModels) {
53
+ return (options: CreateHelperToolLoopAgentOptions) =>
54
+ new ToolLoopAgent({
55
+ id: 'org-memory',
56
+ model: models.chatModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
57
+ headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
58
+ providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
59
+ ...resolveHelperAgentOptions(options),
60
+ })
60
61
  }
@@ -1,7 +1,7 @@
1
1
  import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
2
2
  import { ToolLoopAgent } from 'ai'
3
3
 
4
- import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
4
+ import type { AiGatewayModels } from '../ai-gateway/ai-gateway'
5
5
  import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
6
6
  import type { ResolvedAgentConfig } from '../config/agent-defaults'
7
7
  import {
@@ -76,18 +76,16 @@ Return only the title text. No quotes, labels, JSON, markdown, or explanation.
76
76
  </output>
77
77
  </agent-instructions>`
78
78
 
79
- export function createRecentActivityTitleRefinerAgent(
80
- agentConfig: ResolvedAgentConfig,
81
- options: CreateHelperToolLoopAgentOptions,
82
- ) {
83
- return new ToolLoopAgent({
84
- id: 'recent-activity-title-refiner',
85
- model: aiGatewayChatModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
86
- headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
87
- providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
88
- ...resolveHelperAgentOptions(options, {
89
- instructions: buildRecentActivityTitleRefinerPrompt(agentConfig),
90
- maxOutputTokens: RECENT_ACTIVITY_TITLE_MAX_TOKENS,
91
- }),
92
- })
79
+ export function makeRecentActivityTitleRefinerAgentFactory(models: AiGatewayModels, agentConfig: ResolvedAgentConfig) {
80
+ return (options: CreateHelperToolLoopAgentOptions) =>
81
+ new ToolLoopAgent({
82
+ id: 'recent-activity-title-refiner',
83
+ model: models.chatModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
84
+ headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
85
+ providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
86
+ ...resolveHelperAgentOptions(options, {
87
+ instructions: buildRecentActivityTitleRefinerPrompt(agentConfig),
88
+ maxOutputTokens: RECENT_ACTIVITY_TITLE_MAX_TOKENS,
89
+ }),
90
+ })
93
91
  }
@@ -1,7 +1,7 @@
1
1
  import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
2
2
  import { ToolLoopAgent } from 'ai'
3
3
 
4
- import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
4
+ import type { AiGatewayModels } from '../ai-gateway/ai-gateway'
5
5
  import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
6
6
  import {
7
7
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
@@ -24,15 +24,16 @@ Synthesize an updated workspace profile summary and durable memory facts from co
24
24
  </rules>
25
25
  </agent-instructions>`
26
26
 
27
- export function createRegularChatMemoryDigestAgent(options: CreateHelperToolLoopAgentOptions) {
28
- return new ToolLoopAgent({
29
- id: 'regular-chat-memory-digest',
30
- model: aiGatewayChatModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
31
- headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
32
- providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
33
- ...resolveHelperAgentOptions(options, {
34
- instructions: regularChatMemoryDigestPrompt,
35
- maxOutputTokens: REGULAR_CHAT_MEMORY_DIGEST_MAX_TOKENS,
36
- }),
37
- })
27
+ export function makeRegularChatMemoryDigestAgentFactory(models: AiGatewayModels) {
28
+ return (options: CreateHelperToolLoopAgentOptions) =>
29
+ new ToolLoopAgent({
30
+ id: 'regular-chat-memory-digest',
31
+ model: models.chatModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
32
+ headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
33
+ providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
34
+ ...resolveHelperAgentOptions(options, {
35
+ instructions: regularChatMemoryDigestPrompt,
36
+ maxOutputTokens: REGULAR_CHAT_MEMORY_DIGEST_MAX_TOKENS,
37
+ }),
38
+ })
38
39
  }
@@ -2,7 +2,7 @@ import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
2
2
  import { ToolLoopAgent } from 'ai'
3
3
  import { z } from 'zod'
4
4
 
5
- import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
5
+ import type { AiGatewayModels } from '../ai-gateway/ai-gateway'
6
6
  import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
7
7
  import {
8
8
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
@@ -42,15 +42,16 @@ export const SkillExtractionOutputSchema = z.object({ candidates: z.array(SkillC
42
42
 
43
43
  export type SkillCandidate = z.infer<typeof SkillCandidateSchema>
44
44
 
45
- export function createSkillExtractorAgent(options: CreateHelperToolLoopAgentOptions) {
46
- return new ToolLoopAgent({
47
- id: 'skill-extractor',
48
- model: aiGatewayChatModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
49
- headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
50
- providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
51
- ...resolveHelperAgentOptions(options, {
52
- instructions: skillExtractorPrompt,
53
- maxOutputTokens: SKILL_EXTRACTOR_MAX_TOKENS,
54
- }),
55
- })
45
+ export function makeSkillExtractorAgentFactory(models: AiGatewayModels) {
46
+ return (options: CreateHelperToolLoopAgentOptions) =>
47
+ new ToolLoopAgent({
48
+ id: 'skill-extractor',
49
+ model: models.chatModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
50
+ headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
51
+ providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
52
+ ...resolveHelperAgentOptions(options, {
53
+ instructions: skillExtractorPrompt,
54
+ maxOutputTokens: SKILL_EXTRACTOR_MAX_TOKENS,
55
+ }),
56
+ })
56
57
  }
@@ -2,7 +2,7 @@ import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
2
2
  import { ToolLoopAgent } from 'ai'
3
3
  import { z } from 'zod'
4
4
 
5
- import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
5
+ import type { AiGatewayModels } from '../ai-gateway/ai-gateway'
6
6
  import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
7
7
  import {
8
8
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
@@ -66,15 +66,16 @@ export const SkillManagerOutputSchema = z.object({
66
66
  mergedSkill: MergedSkillSchema.optional(),
67
67
  })
68
68
 
69
- export function createSkillManagerAgent(options: CreateHelperToolLoopAgentOptions) {
70
- return new ToolLoopAgent({
71
- id: 'skill-manager',
72
- model: aiGatewayChatModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
73
- headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
74
- providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
75
- ...resolveHelperAgentOptions(options, {
76
- instructions: skillManagerPrompt,
77
- maxOutputTokens: SKILL_MANAGER_MAX_TOKENS,
78
- }),
79
- })
69
+ export function makeSkillManagerAgentFactory(models: AiGatewayModels) {
70
+ return (options: CreateHelperToolLoopAgentOptions) =>
71
+ new ToolLoopAgent({
72
+ id: 'skill-manager',
73
+ model: models.chatModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
74
+ headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
75
+ providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
76
+ ...resolveHelperAgentOptions(options, {
77
+ instructions: skillManagerPrompt,
78
+ maxOutputTokens: SKILL_MANAGER_MAX_TOKENS,
79
+ }),
80
+ })
80
81
  }
@@ -2,12 +2,12 @@ import { generateObject } from 'ai'
2
2
  import { Effect } from 'effect'
3
3
  import { z } from 'zod'
4
4
 
5
- import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
5
+ import type { AiGatewayModels } from '../ai-gateway/ai-gateway'
6
6
  import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
7
7
  import type { ResolvedAgentConfig } from '../config/agent-defaults'
8
8
  import { chatLogger } from '../config/logger'
9
9
  import type { ValidationError } from '../effect/errors'
10
- import { AiGenerationError } from '../effect/errors'
10
+ import { ERROR_TAGS, AiGenerationError } from '../effect/errors'
11
11
  import { zodParse } from '../effect/zod'
12
12
 
13
13
  const TriageResultSchema = z.object({ agentId: z.string(), routingContext: z.string() })
@@ -170,6 +170,7 @@ function toRouterGenerationError(label: 'triage' | 'check', error: unknown): AiG
170
170
 
171
171
  function generateRouterObjectEffect<TSchema extends z.ZodTypeAny>(params: {
172
172
  agentConfig: ResolvedAgentConfig
173
+ aiGatewayModels: AiGatewayModels
173
174
  schema: TSchema
174
175
  system: string
175
176
  prompt: string
@@ -180,7 +181,7 @@ function generateRouterObjectEffect<TSchema extends z.ZodTypeAny>(params: {
180
181
  return Effect.tryPromise({
181
182
  try: () =>
182
183
  generateObject({
183
- model: aiGatewayChatModel(modelId),
184
+ model: params.aiGatewayModels.chatModel(modelId),
184
185
  headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
185
186
  providerOptions: { openai: { reasoningEffort: 'low' } },
186
187
  schema: params.schema,
@@ -201,12 +202,12 @@ function generateRouterObjectEffect<TSchema extends z.ZodTypeAny>(params: {
201
202
  ),
202
203
  Effect.flatMap(({ object }) => zodParse(params.schema, object)),
203
204
  Effect.catchTags({
204
- AiGenerationError: (error: AiGenerationError) =>
205
+ [ERROR_TAGS.AiGenerationError]: (error: AiGenerationError) =>
205
206
  Effect.sync(() => {
206
207
  chatLogger.error`[thread-router] ${params.label} failed: ${error.message}`
207
208
  return null
208
209
  }),
209
- ValidationError: (error: ValidationError) =>
210
+ [ERROR_TAGS.ValidationError]: (error: ValidationError) =>
210
211
  Effect.sync(() => {
211
212
  chatLogger.error`[thread-router] ${params.label} failed: ${error.message}`
212
213
  return null
@@ -217,6 +218,7 @@ function generateRouterObjectEffect<TSchema extends z.ZodTypeAny>(params: {
217
218
 
218
219
  export function triageThreadMessage(params: {
219
220
  agentConfig: ResolvedAgentConfig
221
+ aiGatewayModels: AiGatewayModels
220
222
  threadTitle: string
221
223
  members: readonly string[]
222
224
  messageText: string
@@ -254,6 +256,7 @@ export function triageThreadMessage(params: {
254
256
 
255
257
  const parsed = yield* generateRouterObjectEffect({
256
258
  agentConfig: params.agentConfig,
259
+ aiGatewayModels: params.aiGatewayModels,
257
260
  schema: TriageResultSchema,
258
261
  system: TRIAGE_SYSTEM_PROMPT,
259
262
  prompt,
@@ -280,6 +283,7 @@ export function triageThreadMessage(params: {
280
283
 
281
284
  export function checkForNextAgent(params: {
282
285
  agentConfig: ResolvedAgentConfig
286
+ aiGatewayModels: AiGatewayModels
283
287
  threadTitle: string
284
288
  members: readonly string[]
285
289
  messageText: string
@@ -323,6 +327,7 @@ export function checkForNextAgent(params: {
323
327
 
324
328
  const parsed = yield* generateRouterObjectEffect({
325
329
  agentConfig: params.agentConfig,
330
+ aiGatewayModels: params.aiGatewayModels,
326
331
  schema: CheckResultObjectSchema,
327
332
  system: CHECK_SYSTEM_PROMPT,
328
333
  prompt,