@lota-sdk/core 0.1.16 → 0.1.18

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 (51) hide show
  1. package/package.json +6 -3
  2. package/src/ai/definitions.ts +1 -1
  3. package/src/ai/embedding-cache.ts +2 -4
  4. package/src/{bifrost/bifrost.ts → ai-gateway/ai-gateway.ts} +115 -79
  5. package/src/ai-gateway/cache-headers.ts +8 -0
  6. package/src/ai-gateway/index.ts +2 -0
  7. package/src/config/model-constants.ts +1 -1
  8. package/src/create-runtime.ts +26 -1
  9. package/src/db/memory-store.helpers.ts +1 -3
  10. package/src/db/schema-fingerprint.ts +1 -3
  11. package/src/embeddings/provider.ts +2 -2
  12. package/src/index.ts +1 -1
  13. package/src/queues/document-processor.queue.ts +2 -4
  14. package/src/queues/post-chat-memory.queue.ts +8 -2
  15. package/src/queues/recent-activity-title-refinement.queue.ts +1 -1
  16. package/src/queues/skill-extraction.queue.ts +1 -1
  17. package/src/queues/workstream-title-generation.queue.ts +1 -1
  18. package/src/redis/redis-lease-lock.ts +1 -2
  19. package/src/runtime/agent-runtime-policy.ts +3 -14
  20. package/src/runtime/context-compaction.ts +2 -4
  21. package/src/runtime/index.ts +1 -1
  22. package/src/runtime/runtime-config.ts +87 -7
  23. package/src/runtime/runtime-extensions.ts +0 -1
  24. package/src/runtime/social-chat.ts +752 -0
  25. package/src/runtime/team-consultation-orchestrator.ts +0 -4
  26. package/src/services/agent-executor.service.ts +0 -1
  27. package/src/services/document-chunk.service.ts +1 -3
  28. package/src/services/index.ts +1 -0
  29. package/src/services/memory.service.ts +7 -2
  30. package/src/services/recent-activity.service.ts +1 -3
  31. package/src/services/social-chat-history.service.ts +197 -0
  32. package/src/services/workstream-message.service.ts +1 -3
  33. package/src/services/workstream-turn-preparation.service.ts +0 -23
  34. package/src/system-agents/context-compaction.agent.ts +4 -2
  35. package/src/system-agents/delegated-agent-factory.ts +3 -0
  36. package/src/system-agents/memory-reranker.agent.ts +5 -3
  37. package/src/system-agents/memory.agent.ts +4 -2
  38. package/src/system-agents/recent-activity-title-refiner.agent.ts +4 -2
  39. package/src/system-agents/regular-chat-memory-digest.agent.ts +4 -2
  40. package/src/system-agents/skill-extractor.agent.ts +4 -2
  41. package/src/system-agents/skill-manager.agent.ts +4 -2
  42. package/src/system-agents/title-generator.agent.ts +4 -2
  43. package/src/tools/research-topic.tool.ts +4 -2
  44. package/src/tools/team-think.tool.ts +0 -3
  45. package/src/workers/regular-chat-memory-digest.helpers.ts +1 -1
  46. package/src/workers/regular-chat-memory-digest.runner.ts +43 -10
  47. package/src/workers/skill-extraction.runner.ts +25 -5
  48. package/src/workers/utils/repo-structure-extractor.ts +2 -2
  49. package/src/workers/utils/workstream-message-query.ts +3 -5
  50. package/src/bifrost/index.ts +0 -1
  51. package/src/runtime/workstream-routing-policy.ts +0 -267
@@ -9,7 +9,6 @@ import type { ReadableUploadMetadataLike } from './chat-attachments'
9
9
  import type { RepoSectionName } from './indexed-repositories-policy'
10
10
  import { buildTeamConsultationFailureMessage } from './team-consultation-prompts'
11
11
  import { extractMessageText } from './workstream-chat-helpers'
12
- import type { ReasoningProfileName } from './workstream-routing-policy'
13
12
 
14
13
  export type DefaultRepoSections = RepoSectionName[]
15
14
  const TEAM_CONSULTATION_TIMEOUT_MS = 90_000
@@ -66,7 +65,6 @@ export interface TeamConsultationParticipantRunner {
66
65
  agentId: string,
67
66
  params: {
68
67
  task: string
69
- reasoningProfile: ReasoningProfileName
70
68
  systemWorkspaceDetails?: string
71
69
  preSeededMemoriesSection?: string
72
70
  retrievedKnowledgeSection?: string
@@ -90,7 +88,6 @@ export interface CreateConsultTeamToolParams {
90
88
  latestUserMessageId: string
91
89
  availableUploads: ReadableUploadMetadataLike[]
92
90
  defaultRepoSectionsByAgent: Record<string, DefaultRepoSections | undefined>
93
- reasoningProfile: 'fast' | 'standard' | 'deep'
94
91
  systemWorkspaceDetails?: string
95
92
  getPreSeededMemoriesSection: (agentId: string) => Promise<string | undefined>
96
93
  retrievedKnowledgeSection?: string
@@ -135,7 +132,6 @@ export function createConsultTeamTool(params: CreateConsultTeamToolParams) {
135
132
  try {
136
133
  const { agent, observer } = await params.participantRunner.buildParticipantAgent(agentId, {
137
134
  task,
138
- reasoningProfile: params.reasoningProfile,
139
135
  systemWorkspaceDetails: params.systemWorkspaceDetails,
140
136
  preSeededMemoriesSection: await params.getPreSeededMemoriesSection(agentId),
141
137
  retrievedKnowledgeSection: params.retrievedKnowledgeSection,
@@ -146,7 +146,6 @@ class AgentExecutorService {
146
146
  mode: dispatchMode,
147
147
  onboardingActive: false,
148
148
  linearInstalled: Boolean(linearInstallation),
149
- reasoningProfile: 'standard',
150
149
  additionalInstructionSections: [
151
150
  buildOwnershipDispatchContextSection({
152
151
  node: params.nodeSpec,
@@ -1,5 +1,3 @@
1
- import { createHash } from 'node:crypto'
2
-
3
1
  import { chunkMarkdownDocument, chunkPagedDocument, chunkPlainTextDocument } from '../document/org-document-chunking'
4
2
  import type { ParsedDocumentChunk } from '../document/org-document-chunking'
5
3
  import { getDefaultEmbeddings } from '../embeddings/provider'
@@ -56,7 +54,7 @@ export class DocumentChunkService {
56
54
  }
57
55
 
58
56
  hashContent(content: string): string {
59
- return createHash('sha256').update(content).digest('hex')
57
+ return new Bun.CryptoHasher('sha256').update(content).digest('hex')
60
58
  }
61
59
 
62
60
  // Uses 4 chars/token (conservative estimate for document content which tends
@@ -30,6 +30,7 @@ export * from './plugin-executor.service'
30
30
  export * from './recent-activity-title.service'
31
31
  export * from './recent-activity.service'
32
32
  export * from './skill-resolver.service'
33
+ export * from './social-chat-history.service'
33
34
  export * from './system-executor.service'
34
35
  export * from './user.service'
35
36
  export * from './workstream-message.service'
@@ -722,6 +722,8 @@ class MemoryService {
722
722
  input,
723
723
  output,
724
724
  sourceId,
725
+ source = 'chat',
726
+ sourceMetadata,
725
727
  onboardStatus,
726
728
  agentName,
727
729
  historyMessages = [],
@@ -733,6 +735,8 @@ class MemoryService {
733
735
  input: string
734
736
  output: string
735
737
  sourceId?: string
738
+ source?: string
739
+ sourceMetadata?: Record<string, unknown>
736
740
  onboardStatus?: string
737
741
  agentName?: string
738
742
  historyMessages?: Array<{ role: 'user' | 'agent'; content: string; agentName?: string }>
@@ -789,7 +793,7 @@ class MemoryService {
789
793
  scopeId: orgScopeId,
790
794
  memoryType: ORG_MEMORY_TYPE,
791
795
  ...(typeof assessedImportance === 'number' ? { importance: assessedImportance } : {}),
792
- metadata: { orgId, source: 'chat', ...(sourceId ? { sourceId } : {}), ...assessmentMetadata },
796
+ metadata: { orgId, source, ...(sourceId ? { sourceId } : {}), ...sourceMetadata, ...assessmentMetadata },
793
797
  },
794
798
  ]
795
799
 
@@ -803,8 +807,9 @@ class MemoryService {
803
807
  orgId,
804
808
  agentName: scopedAgentName,
805
809
  memoryScope: 'agent',
806
- source: 'chat',
810
+ source,
807
811
  ...(sourceId ? { sourceId } : {}),
812
+ ...sourceMetadata,
808
813
  ...assessmentMetadata,
809
814
  },
810
815
  })
@@ -1,5 +1,3 @@
1
- import { createHash } from 'node:crypto'
2
-
3
1
  import {
4
2
  RecentActivityDeepLinkSchema,
5
3
  RecentActivityEventInputSchema,
@@ -70,7 +68,7 @@ type RecentActivityEventRow = z.infer<typeof RecentActivityEventRowSchema>
70
68
  type RecentActivityRow = z.infer<typeof RecentActivityRowSchema>
71
69
 
72
70
  function buildDeterministicRecordId(table: string, key: string): RecordId {
73
- const digest = createHash('sha256').update(key).digest('hex')
71
+ const digest = new Bun.CryptoHasher('sha256').update(key).digest('hex')
74
72
  return new RecordId(table, digest)
75
73
  }
76
74
 
@@ -0,0 +1,197 @@
1
+ import { z } from 'zod'
2
+
3
+ import { getRedisConnection } from '../redis'
4
+ import type { LotaRuntimeBackgroundCursor, LotaRuntimeBackgroundCursorKind } from '../runtime/runtime-extensions'
5
+
6
+ const DEFAULT_SOCIAL_CHAT_HISTORY_PREFIX = 'lota:social:history'
7
+
8
+ const SocialChatMessageRoleSchema = z.enum(['user', 'assistant'])
9
+ const SocialChatSourceSchema = z.literal('social')
10
+ const SocialChatPlatformSchema = z.literal('slack')
11
+
12
+ const SocialChatHistoryMessageSchema = z.object({
13
+ source: SocialChatSourceSchema,
14
+ sourceId: z.string().trim().min(1),
15
+ platform: SocialChatPlatformSchema,
16
+ workspaceId: z.string().trim().min(1),
17
+ channelId: z.string().trim().min(1),
18
+ threadId: z.string().trim().min(1),
19
+ messageId: z.string().trim().min(1),
20
+ role: SocialChatMessageRoleSchema,
21
+ parts: z.array(z.record(z.string(), z.unknown())),
22
+ metadata: z.record(z.string(), z.unknown()).optional(),
23
+ cursor: z.object({ createdAt: z.coerce.date(), id: z.string().trim().min(1) }),
24
+ })
25
+
26
+ export type SocialChatHistoryMessage = z.infer<typeof SocialChatHistoryMessageSchema>
27
+
28
+ let socialChatHistoryPrefix = DEFAULT_SOCIAL_CHAT_HISTORY_PREFIX
29
+
30
+ function trimConfiguredPrefix(value: string | undefined): string {
31
+ const normalized = value?.trim()
32
+ return normalized && normalized.length > 0 ? normalized : DEFAULT_SOCIAL_CHAT_HISTORY_PREFIX
33
+ }
34
+
35
+ function compareCursorOrder(left: LotaRuntimeBackgroundCursor, right: LotaRuntimeBackgroundCursor): number {
36
+ const timeDiff = left.createdAt.getTime() - right.createdAt.getTime()
37
+ if (timeDiff !== 0) return timeDiff
38
+ return left.id.localeCompare(right.id)
39
+ }
40
+
41
+ class SocialChatHistoryService {
42
+ configure(params?: { keyPrefix?: string }): void {
43
+ socialChatHistoryPrefix = trimConfiguredPrefix(params?.keyPrefix)
44
+ }
45
+
46
+ private messageStorageKey(message: {
47
+ platform: 'slack'
48
+ workspaceId: string
49
+ threadId: string
50
+ messageId: string
51
+ }): string {
52
+ return `${socialChatHistoryPrefix}:message:${message.platform}:${message.workspaceId}:${message.threadId}:${message.messageId}`
53
+ }
54
+
55
+ private threadIndexKey(workspaceId: string, threadId: string): string {
56
+ return `${socialChatHistoryPrefix}:thread:${workspaceId}:${threadId}`
57
+ }
58
+
59
+ private workspaceIndexKey(workspaceId: string): string {
60
+ return `${socialChatHistoryPrefix}:workspace:${workspaceId}`
61
+ }
62
+
63
+ private cursorKey(kind: LotaRuntimeBackgroundCursorKind, workspaceId: string): string {
64
+ return `${socialChatHistoryPrefix}:cursor:${kind}:${workspaceId}`
65
+ }
66
+
67
+ private serializeMessage(message: SocialChatHistoryMessage): string {
68
+ return JSON.stringify({
69
+ ...message,
70
+ cursor: { ...message.cursor, createdAt: message.cursor.createdAt.toISOString() },
71
+ })
72
+ }
73
+
74
+ private parseStoredMessage(value: string | null): SocialChatHistoryMessage | null {
75
+ if (!value) return null
76
+ try {
77
+ const parsed = SocialChatHistoryMessageSchema.safeParse(JSON.parse(value))
78
+ return parsed.success ? parsed.data : null
79
+ } catch {
80
+ return null
81
+ }
82
+ }
83
+
84
+ private serializeCursor(cursor: LotaRuntimeBackgroundCursor): string {
85
+ return JSON.stringify({ ...cursor, createdAt: cursor.createdAt.toISOString() })
86
+ }
87
+
88
+ private parseCursor(value: string | null): LotaRuntimeBackgroundCursor | null {
89
+ if (!value) return null
90
+ try {
91
+ const parsed = z.object({ createdAt: z.coerce.date(), id: z.string().trim().min(1) }).safeParse(JSON.parse(value))
92
+ return parsed.success ? parsed.data : null
93
+ } catch {
94
+ return null
95
+ }
96
+ }
97
+
98
+ async upsertMessages(messages: SocialChatHistoryMessage[]): Promise<SocialChatHistoryMessage[]> {
99
+ if (messages.length === 0) return []
100
+
101
+ const redis = getRedisConnection()
102
+ const normalizedMessages = messages.map((message) => SocialChatHistoryMessageSchema.parse(message))
103
+ const multi = redis.multi()
104
+
105
+ for (const message of normalizedMessages) {
106
+ const storageKey = this.messageStorageKey(message)
107
+ const score = message.cursor.createdAt.getTime()
108
+ multi.set(storageKey, this.serializeMessage(message))
109
+ multi.zadd(this.threadIndexKey(message.workspaceId, message.threadId), score, storageKey)
110
+ multi.zadd(this.workspaceIndexKey(message.workspaceId), score, storageKey)
111
+ }
112
+
113
+ await multi.exec()
114
+ return normalizedMessages
115
+ }
116
+
117
+ async listThreadMessages(params: { workspaceId: string; threadId: string }): Promise<SocialChatHistoryMessage[]> {
118
+ const redis = getRedisConnection()
119
+ const storageKeys = await redis.zrange(this.threadIndexKey(params.workspaceId, params.threadId), 0, -1)
120
+ if (storageKeys.length === 0) return []
121
+
122
+ const storedValues = await redis.mget(storageKeys)
123
+ return storedValues
124
+ .map((value) => this.parseStoredMessage(value))
125
+ .filter((message): message is SocialChatHistoryMessage => message !== null)
126
+ .sort((left, right) => compareCursorOrder(left.cursor, right.cursor))
127
+ }
128
+
129
+ async listWorkspaceMessages(params: {
130
+ workspaceId: string
131
+ cursor: LotaRuntimeBackgroundCursor | null
132
+ onboardingCutoff: Date | null
133
+ }): Promise<SocialChatHistoryMessage[]> {
134
+ const redis = getRedisConnection()
135
+ const indexKey = this.workspaceIndexKey(params.workspaceId)
136
+ const scoreStart =
137
+ params.cursor?.createdAt.getTime() ??
138
+ (params.onboardingCutoff ? params.onboardingCutoff.getTime() : Number.NEGATIVE_INFINITY)
139
+ const storageKeys =
140
+ params.cursor || params.onboardingCutoff
141
+ ? await redis.zrangebyscore(indexKey, scoreStart, '+inf')
142
+ : await redis.zrange(indexKey, 0, -1)
143
+
144
+ if (storageKeys.length === 0) return []
145
+
146
+ const storedValues = await redis.mget(storageKeys)
147
+ return storedValues
148
+ .map((value) => this.parseStoredMessage(value))
149
+ .filter((message): message is SocialChatHistoryMessage => message !== null)
150
+ .filter((message) => {
151
+ if (params.cursor) {
152
+ return compareCursorOrder(message.cursor, params.cursor) > 0
153
+ }
154
+ if (params.onboardingCutoff) {
155
+ return message.cursor.createdAt.getTime() > params.onboardingCutoff.getTime()
156
+ }
157
+ return true
158
+ })
159
+ .sort((left, right) => compareCursorOrder(left.cursor, right.cursor))
160
+ }
161
+
162
+ async hasWorkspaceMessages(params: {
163
+ workspaceId: string
164
+ cursor: LotaRuntimeBackgroundCursor | null
165
+ onboardingCutoff: Date | null
166
+ }): Promise<boolean> {
167
+ const messages = await this.listWorkspaceMessages({
168
+ workspaceId: params.workspaceId,
169
+ cursor: params.cursor,
170
+ onboardingCutoff: params.onboardingCutoff,
171
+ })
172
+ return messages.length > 0
173
+ }
174
+
175
+ async getBackgroundCursor(
176
+ kind: LotaRuntimeBackgroundCursorKind,
177
+ workspaceId: string,
178
+ ): Promise<LotaRuntimeBackgroundCursor | null> {
179
+ const redis = getRedisConnection()
180
+ return this.parseCursor(await redis.get(this.cursorKey(kind, workspaceId)))
181
+ }
182
+
183
+ async setBackgroundCursor(
184
+ kind: LotaRuntimeBackgroundCursorKind,
185
+ workspaceId: string,
186
+ cursor: LotaRuntimeBackgroundCursor,
187
+ ): Promise<void> {
188
+ const redis = getRedisConnection()
189
+ await redis.set(this.cursorKey(kind, workspaceId), this.serializeCursor(cursor))
190
+ }
191
+ }
192
+
193
+ export const socialChatHistoryService = new SocialChatHistoryService()
194
+
195
+ export function configureSocialChatHistory(params?: { keyPrefix?: string }): void {
196
+ socialChatHistoryService.configure(params)
197
+ }
@@ -1,5 +1,3 @@
1
- import { createHash } from 'node:crypto'
2
-
3
1
  import { parseRowMetadata, recordIdSchema, requireTimestamp, withCreatedAtMetadata } from '@lota-sdk/shared'
4
2
  import type { ChatMessage } from '@lota-sdk/shared'
5
3
  import { RecordId, surql } from 'surrealdb'
@@ -30,7 +28,7 @@ function toMessageId(value: string | RecordIdRef): string {
30
28
  */
31
29
  function toWorkstreamMessageRowId(workstreamId: RecordIdRef, messageId: string): RecordId {
32
30
  const workstreamStr = recordIdToString(workstreamId, TABLES.WORKSTREAM)
33
- const digest = createHash('sha256').update(`${workstreamStr}\0${messageId}`).digest('hex').slice(0, 32)
31
+ const digest = new Bun.CryptoHasher('sha256').update(`${workstreamStr}\0${messageId}`).digest('hex').slice(0, 32)
34
32
  return new RecordId(TABLES.WORKSTREAM_MESSAGE, digest)
35
33
  }
36
34
 
@@ -69,11 +69,6 @@ import {
69
69
  toHistoryMessages,
70
70
  toOptionalTrimmedString,
71
71
  } from '../runtime/workstream-chat-helpers'
72
- import {
73
- classifyHighImpactResponse,
74
- classifyPolicyClasses,
75
- resolveReasoningProfile,
76
- } from '../runtime/workstream-routing-policy'
77
72
  import type { WorkstreamState } from '../runtime/workstream-state'
78
73
  import { chatRunRegistry } from '../services/chat-run-registry.service'
79
74
  import type { NormalizedWorkstream, WorkstreamRecord } from '../services/workstream.types'
@@ -274,7 +269,6 @@ interface StreamAgentResponseContext {
274
269
  onboardingActive: boolean
275
270
  linearInstalled: boolean
276
271
  githubInstalled: boolean
277
- reasoningProfileName: string
278
272
  buildContextResult: Record<string, unknown> | null
279
273
  getExecutionPlanInstructionSections: () => Promise<string[] | undefined>
280
274
  getPreSeededMemoriesSection: (agentId: string) => Promise<string | undefined>
@@ -324,7 +318,6 @@ async function streamAgentResponse(
324
318
  onboardingActive: ctx.onboardingActive,
325
319
  linearInstalled: ctx.linearInstalled,
326
320
  githubInstalled: ctx.githubInstalled,
327
- reasoningProfile: ctx.reasoningProfileName,
328
321
  skills: streamParams.skills,
329
322
  additionalInstructionSections: streamParams.additionalInstructionSections,
330
323
  context: ctx.buildContextResult,
@@ -345,7 +338,6 @@ async function streamAgentResponse(
345
338
  skills: streamParams.skills,
346
339
  onboardingActive: ctx.onboardingActive,
347
340
  linearInstalled: ctx.linearInstalled,
348
- reasoningProfile: ctx.reasoningProfileName,
349
341
  systemWorkspaceDetails: ctx.promptContext.systemWorkspaceDetails,
350
342
  preSeededMemoriesSection,
351
343
  retrievedKnowledgeSection: ctx.retrievedKnowledgeSection,
@@ -753,15 +745,6 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
753
745
  'getInstallationForOrganization',
754
746
  ])
755
747
 
756
- const highImpactAssessment = classifyHighImpactResponse({ message: messageText })
757
- const policyAssessment = classifyPolicyClasses({ message: messageText })
758
- const reasoningProfile = resolveReasoningProfile({
759
- message: messageText,
760
- forceDeep: highImpactAssessment.classes.length > 0 || policyAssessment.classes.length > 0,
761
- explicitProfile: onboardingActive ? 'standard' : undefined,
762
- })
763
- timer.step('reasoning-classification')
764
-
765
748
  const [linearInstallation, githubInstallation, indexedRepoContext, recentDomainEvents, promptSummary] =
766
749
  await Promise.all([
767
750
  getLinearInstallationByOrgId ? (getLinearInstallationByOrgId(orgRef) as Promise<unknown>) : Promise.resolve(null),
@@ -968,9 +951,6 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
968
951
  const buildAgentMetadataPatch = (agentId: string, agentName: string): NonNullable<MessageMetadata> => ({
969
952
  agentId,
970
953
  agentName,
971
- reasoningProfile: reasoningProfile.name,
972
- highImpactClasses: highImpactAssessment.classes,
973
- policyClasses: policyAssessment.classes,
974
954
  semanticTerminationReason: 'none',
975
955
  })
976
956
 
@@ -1007,7 +987,6 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
1007
987
  onboardingActive,
1008
988
  linearInstalled,
1009
989
  githubInstalled,
1010
- reasoningProfileName: reasoningProfile.name,
1011
990
  buildContextResult,
1012
991
  getExecutionPlanInstructionSections,
1013
992
  getPreSeededMemoriesSection,
@@ -1137,7 +1116,6 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
1137
1116
  mode: 'fixedWorkstreamMode',
1138
1117
  onboardingActive,
1139
1118
  linearInstalled,
1140
- reasoningProfile: reasoningProfile.name,
1141
1119
  systemWorkspaceDetails: promptContext.systemWorkspaceDetails,
1142
1120
  preSeededMemoriesSection: specialistPreSeededMemories,
1143
1121
  retrievedKnowledgeSection,
@@ -1234,7 +1212,6 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
1234
1212
  availableUploads: listReadableUploads(),
1235
1213
  provideRepoTool: indexedRepoContext.provideRepoTool,
1236
1214
  defaultRepoSectionsByAgent: indexedRepoContext.defaultSectionsByAgent as never,
1237
- reasoningProfile: reasoningProfile.name,
1238
1215
  systemWorkspaceDetails: promptContext.systemWorkspaceDetails,
1239
1216
  getPreSeededMemoriesSection,
1240
1217
  retrievedKnowledgeSection,
@@ -1,6 +1,7 @@
1
1
  import { ToolLoopAgent } from 'ai'
2
2
 
3
- import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
3
+ import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
4
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
4
5
  import {
5
6
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
6
7
  OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
@@ -39,7 +40,8 @@ Return valid data for:
39
40
  export function createContextCompactionAgent(options: CreateHelperToolLoopAgentOptions) {
40
41
  return new ToolLoopAgent({
41
42
  id: 'context-compaction',
42
- model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
43
+ model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
44
+ headers: buildAiGatewayCacheHeaders('context-compaction'),
43
45
  providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
44
46
  ...resolveHelperAgentOptions(options, { instructions: CONTEXT_COMPACTION_PROMPT }),
45
47
  })
@@ -17,6 +17,7 @@ interface DelegatedAgentDefinition {
17
17
  providerOptions?: AgentProviderOptions
18
18
  instructions: string
19
19
  tools?: ToolSet
20
+ headers?: Record<string, string>
20
21
  maxSteps?: number
21
22
  maxOutputTokens?: number
22
23
  temperature?: number
@@ -165,6 +166,7 @@ export function createDelegatedAgentTool(definition: DelegatedAgentDefinition):
165
166
  id: definition.id,
166
167
  model: resolveAgentModel(definition.model),
167
168
  ...(definition.providerOptions ? { providerOptions: definition.providerOptions } : {}),
169
+ ...(definition.headers ? { headers: definition.headers } : {}),
168
170
  instructions: buildDelegatedAgentInstructions(definition.instructions, agentTools),
169
171
  tools: agentTools,
170
172
  maxOutputTokens: definition.maxOutputTokens ?? DEFAULT_DELEGATED_AGENT_MAX_OUTPUT_TOKENS,
@@ -206,6 +208,7 @@ export function createDelegatedAgentToolWithContext<TContext>(
206
208
  id: definition.id,
207
209
  model: resolveAgentModel(definition.model),
208
210
  ...(definition.providerOptions ? { providerOptions: definition.providerOptions } : {}),
211
+ ...(definition.headers ? { headers: definition.headers } : {}),
209
212
  instructions: buildDelegatedAgentInstructions(definition.instructions, agentTools),
210
213
  tools: agentTools,
211
214
  maxOutputTokens: definition.maxOutputTokens ?? DEFAULT_DELEGATED_AGENT_MAX_OUTPUT_TOKENS,
@@ -1,9 +1,10 @@
1
1
  import { ToolLoopAgent } from 'ai'
2
2
 
3
- import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
3
+ import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
4
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
4
5
  import {
5
6
  OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS,
6
- OPENROUTER_STRUCTURED_REASONING_MODEL_ID,
7
+ OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
7
8
  } from '../config/model-constants'
8
9
  import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
9
10
  import { resolveHelperAgentOptions } from './helper-agent-options'
@@ -31,7 +32,8 @@ Set every item.relevance as a string; use empty string when no reason is needed.
31
32
  export function createMemoryRerankerAgent(options: CreateHelperToolLoopAgentOptions) {
32
33
  return new ToolLoopAgent({
33
34
  id: 'memory-reranker',
34
- model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_REASONING_MODEL_ID),
35
+ model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
36
+ headers: buildAiGatewayCacheHeaders('memory-reranker'),
35
37
  providerOptions: OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS,
36
38
  ...resolveHelperAgentOptions(options),
37
39
  })
@@ -1,6 +1,7 @@
1
1
  import { ToolLoopAgent } from 'ai'
2
2
 
3
- import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
3
+ import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
4
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
4
5
  import {
5
6
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
6
7
  OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
@@ -51,7 +52,8 @@ Return only the schema fields with no extra formatting.
51
52
  export function createOrgMemoryAgent(options: CreateHelperToolLoopAgentOptions) {
52
53
  return new ToolLoopAgent({
53
54
  id: 'org-memory',
54
- model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
55
+ model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
56
+ headers: buildAiGatewayCacheHeaders('org-memory'),
55
57
  providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
56
58
  ...resolveHelperAgentOptions(options),
57
59
  })
@@ -1,6 +1,7 @@
1
1
  import { ToolLoopAgent } from 'ai'
2
2
 
3
- import { bifrostModel } from '../bifrost/bifrost'
3
+ import { aiGatewayModel } from '../ai-gateway/ai-gateway'
4
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
4
5
  import { getLeadAgentDisplayName } from '../config/agent-defaults'
5
6
  import {
6
7
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
@@ -78,7 +79,8 @@ Return only the title text. No quotes, labels, JSON, markdown, or explanation.
78
79
  export function createRecentActivityTitleRefinerAgent(options: CreateHelperToolLoopAgentOptions) {
79
80
  return new ToolLoopAgent({
80
81
  id: 'recent-activity-title-refiner',
81
- model: bifrostModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
82
+ model: aiGatewayModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
83
+ headers: buildAiGatewayCacheHeaders('recent-activity-title-refiner'),
82
84
  providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
83
85
  ...resolveHelperAgentOptions(options, {
84
86
  instructions: buildRecentActivityTitleRefinerPrompt(),
@@ -1,6 +1,7 @@
1
1
  import { ToolLoopAgent } from 'ai'
2
2
 
3
- import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
3
+ import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
4
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
4
5
  import {
5
6
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
6
7
  OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
@@ -65,7 +66,8 @@ Return only schema fields.
65
66
  export function createRegularChatMemoryDigestAgent(options: CreateHelperToolLoopAgentOptions) {
66
67
  return new ToolLoopAgent({
67
68
  id: 'regular-chat-memory-digest',
68
- model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
69
+ model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
70
+ headers: buildAiGatewayCacheHeaders('regular-chat-memory-digest'),
69
71
  providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
70
72
  ...resolveHelperAgentOptions(options, {
71
73
  instructions: regularChatMemoryDigestPrompt,
@@ -1,7 +1,8 @@
1
1
  import { ToolLoopAgent } from 'ai'
2
2
  import { z } from 'zod'
3
3
 
4
- import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
4
+ import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
5
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
5
6
  import {
6
7
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
7
8
  OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
@@ -78,7 +79,8 @@ export type SkillCandidate = z.infer<typeof SkillCandidateSchema>
78
79
  export function createSkillExtractorAgent(options: CreateHelperToolLoopAgentOptions) {
79
80
  return new ToolLoopAgent({
80
81
  id: 'skill-extractor',
81
- model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
82
+ model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
83
+ headers: buildAiGatewayCacheHeaders('skill-extractor'),
82
84
  providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
83
85
  ...resolveHelperAgentOptions(options, {
84
86
  instructions: skillExtractorPrompt,
@@ -1,7 +1,8 @@
1
1
  import { ToolLoopAgent } from 'ai'
2
2
  import { z } from 'zod'
3
3
 
4
- import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
4
+ import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
5
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
5
6
  import {
6
7
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
7
8
  OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
@@ -68,7 +69,8 @@ export const SkillManagerOutputSchema = z.object({
68
69
  export function createSkillManagerAgent(options: CreateHelperToolLoopAgentOptions) {
69
70
  return new ToolLoopAgent({
70
71
  id: 'skill-manager',
71
- model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
72
+ model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
73
+ headers: buildAiGatewayCacheHeaders('skill-manager'),
72
74
  providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
73
75
  ...resolveHelperAgentOptions(options, {
74
76
  instructions: skillManagerPrompt,
@@ -1,6 +1,7 @@
1
1
  import { ToolLoopAgent } from 'ai'
2
2
 
3
- import { bifrostModel } from '../bifrost/bifrost'
3
+ import { aiGatewayModel } from '../ai-gateway/ai-gateway'
4
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
4
5
  import {
5
6
  OPENROUTER_FAST_REASONING_MODEL_ID,
6
7
  OPENROUTER_MINIMAL_REASONING_PROVIDER_OPTIONS,
@@ -32,7 +33,8 @@ Return only the title text. No quotes, no labels, no explanation.
32
33
  export function createWorkstreamTitleGeneratorAgent(options: CreateHelperToolLoopAgentOptions) {
33
34
  return new ToolLoopAgent({
34
35
  id: 'workstream-title-generator',
35
- model: bifrostModel(OPENROUTER_FAST_REASONING_MODEL_ID),
36
+ model: aiGatewayModel(OPENROUTER_FAST_REASONING_MODEL_ID),
37
+ headers: buildAiGatewayCacheHeaders('workstream-title-generator'),
36
38
  providerOptions: OPENROUTER_MINIMAL_REASONING_PROVIDER_OPTIONS,
37
39
  ...resolveHelperAgentOptions(options, {
38
40
  instructions: WORKSTREAM_TITLE_GENERATOR_PROMPT,
@@ -1,4 +1,5 @@
1
- import { bifrostChatModel } from '../bifrost/bifrost'
1
+ import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
2
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
2
3
  import {
3
4
  OPENROUTER_MEDIUM_REASONING_PROVIDER_OPTIONS,
4
5
  OPENROUTER_WEB_RESEARCH_MODEL_ID,
@@ -12,8 +13,9 @@ export const researchTopicTool = createDelegatedAgentTool({
12
13
  id: 'researchTopic',
13
14
  description:
14
15
  'Delegate a research task to a dedicated research agent that searches the web, fetches pages, and returns a synthesized markdown report. Call multiple instances in parallel for broad research across different topics.',
15
- model: () => bifrostChatModel(OPENROUTER_WEB_RESEARCH_MODEL_ID),
16
+ model: () => aiGatewayChatModel(OPENROUTER_WEB_RESEARCH_MODEL_ID),
16
17
  providerOptions: OPENROUTER_MEDIUM_REASONING_PROVIDER_OPTIONS,
18
+ headers: buildAiGatewayCacheHeaders('researchTopic'),
17
19
  instructions: RESEARCHER_PROMPT,
18
20
  tools: { searchWeb: searchWebTool.create(), fetchWebpage: fetchWebpageTool.create() },
19
21
  })
@@ -40,7 +40,6 @@ export function createTeamThinkTool(params: {
40
40
  availableUploads: ReadableUploadMetadata[]
41
41
  provideRepoTool: boolean
42
42
  defaultRepoSectionsByAgent: Record<string, DefaultRepoSections | undefined>
43
- reasoningProfile: 'fast' | 'standard' | 'deep'
44
43
  systemWorkspaceDetails?: string
45
44
  getPreSeededMemoriesSection: (agentId: string) => Promise<string | undefined>
46
45
  retrievedKnowledgeSection?: string
@@ -59,7 +58,6 @@ export function createTeamThinkTool(params: {
59
58
  mode: 'fixedWorkstreamMode',
60
59
  onboardingActive: false,
61
60
  linearInstalled: false,
62
- reasoningProfile: 'fast',
63
61
  systemWorkspaceDetails: runParams.systemWorkspaceDetails,
64
62
  preSeededMemoriesSection: runParams.preSeededMemoriesSection,
65
63
  retrievedKnowledgeSection: runParams.retrievedKnowledgeSection,
@@ -116,7 +114,6 @@ export function createTeamThinkTool(params: {
116
114
  latestUserMessageId: params.latestUserMessageId,
117
115
  availableUploads: params.availableUploads,
118
116
  defaultRepoSectionsByAgent: params.defaultRepoSectionsByAgent,
119
- reasoningProfile: params.reasoningProfile,
120
117
  systemWorkspaceDetails: params.systemWorkspaceDetails,
121
118
  getPreSeededMemoriesSection: params.getPreSeededMemoriesSection,
122
119
  retrievedKnowledgeSection: params.retrievedKnowledgeSection,
@@ -2,7 +2,7 @@ import { isAgentName } from '../config/agent-defaults'
2
2
  import { compactWhitespace } from '../utils/string'
3
3
 
4
4
  interface DigestMessageForTranscript {
5
- source: 'workstream'
5
+ source: 'workstream' | 'social'
6
6
  sourceId: string
7
7
  role: 'system' | 'user' | 'assistant'
8
8
  parts: Array<Record<string, unknown>>