@lota-sdk/core 0.1.17 → 0.1.19
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 +2 -2
- package/src/ai-gateway/ai-gateway.ts +431 -0
- package/src/ai-gateway/cache-headers.ts +33 -0
- package/src/{bifrost → ai-gateway}/index.ts +1 -1
- package/src/config/model-constants.ts +1 -1
- package/src/embeddings/provider.ts +2 -2
- package/src/index.ts +1 -1
- package/src/runtime/chat-run-registry.ts +4 -0
- package/src/runtime/context-compaction.ts +100 -12
- package/src/runtime/memory-prompts-fact.ts +3 -1
- package/src/runtime/runtime-config.ts +0 -4
- package/src/services/workstream-title.service.ts +1 -1
- package/src/services/workstream-turn-preparation.service.ts +4 -1
- package/src/services/workstream.service.ts +19 -2
- package/src/system-agents/context-compaction.agent.ts +4 -4
- package/src/system-agents/delegated-agent-factory.ts +2 -9
- package/src/system-agents/memory-reranker.agent.ts +4 -4
- package/src/system-agents/memory.agent.ts +4 -4
- package/src/system-agents/recent-activity-title-refiner.agent.ts +4 -4
- package/src/system-agents/regular-chat-memory-digest.agent.ts +4 -4
- package/src/system-agents/skill-extractor.agent.ts +4 -4
- package/src/system-agents/skill-manager.agent.ts +4 -4
- package/src/system-agents/title-generator.agent.ts +4 -4
- package/src/tools/research-topic.tool.ts +4 -4
- package/src/utils/date-time.ts +11 -0
- package/src/workers/utils/file-section-chunker.ts +1 -1
- package/src/bifrost/bifrost.ts +0 -325
- package/src/bifrost/cache-headers.ts +0 -8
|
@@ -101,6 +101,90 @@ function sanitizeStateText(value: string): string | null {
|
|
|
101
101
|
return normalized
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
function buildExistingWorkstreamStateForCompactionPrompt(state: WorkstreamState): Record<string, unknown> {
|
|
105
|
+
const currentPlanText = state.currentPlan ? sanitizeStateText(state.currentPlan.text) : null
|
|
106
|
+
|
|
107
|
+
const activeConstraints = state.activeConstraints
|
|
108
|
+
.map((constraint) => ({
|
|
109
|
+
id: constraint.id,
|
|
110
|
+
text: sanitizeStateText(constraint.text),
|
|
111
|
+
source: constraint.source,
|
|
112
|
+
approved: constraint.approved,
|
|
113
|
+
sourceMessageIds: constraint.sourceMessageIds,
|
|
114
|
+
}))
|
|
115
|
+
.filter((constraint): constraint is typeof constraint & { text: string } => Boolean(constraint.text))
|
|
116
|
+
|
|
117
|
+
const keyDecisions = state.keyDecisions
|
|
118
|
+
.map((decision) => ({
|
|
119
|
+
id: decision.id,
|
|
120
|
+
decision: sanitizeStateText(decision.decision),
|
|
121
|
+
rationale: sanitizeStateText(decision.rationale),
|
|
122
|
+
agent: decision.agent,
|
|
123
|
+
sourceMessageIds: decision.sourceMessageIds,
|
|
124
|
+
confidence: decision.confidence,
|
|
125
|
+
}))
|
|
126
|
+
.filter((decision): decision is typeof decision & { decision: string; rationale: string } =>
|
|
127
|
+
Boolean(decision.decision && decision.rationale),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
const tasks = state.tasks
|
|
131
|
+
.map((task) => ({
|
|
132
|
+
id: task.id,
|
|
133
|
+
title: sanitizeStateText(task.title),
|
|
134
|
+
status: task.status,
|
|
135
|
+
owner: sanitizeStateText(task.owner),
|
|
136
|
+
externalId: task.externalId,
|
|
137
|
+
source: task.source,
|
|
138
|
+
sourceMessageIds: task.sourceMessageIds,
|
|
139
|
+
}))
|
|
140
|
+
.filter((task): task is typeof task & { title: string; owner: string } => Boolean(task.title && task.owner))
|
|
141
|
+
|
|
142
|
+
const openQuestions = state.openQuestions
|
|
143
|
+
.map((question) => ({
|
|
144
|
+
id: question.id,
|
|
145
|
+
text: sanitizeStateText(question.text),
|
|
146
|
+
source: question.source,
|
|
147
|
+
sourceMessageIds: question.sourceMessageIds,
|
|
148
|
+
}))
|
|
149
|
+
.filter((question): question is typeof question & { text: string } => Boolean(question.text))
|
|
150
|
+
|
|
151
|
+
const artifacts = state.artifacts
|
|
152
|
+
.map((artifact) => ({
|
|
153
|
+
id: artifact.id,
|
|
154
|
+
name: sanitizeStateText(artifact.name),
|
|
155
|
+
type: artifact.type,
|
|
156
|
+
pointer: sanitizeStateText(artifact.pointer),
|
|
157
|
+
}))
|
|
158
|
+
.filter((artifact): artifact is typeof artifact & { name: string; pointer: string } =>
|
|
159
|
+
Boolean(artifact.name && artifact.pointer),
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
const agentContributions = state.agentContributions
|
|
163
|
+
.map((note) => ({ id: note.id, agent: note.agent, summary: sanitizeStateText(note.summary) }))
|
|
164
|
+
.filter((note): note is typeof note & { summary: string } => Boolean(note.summary))
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
currentPlan: currentPlanText
|
|
168
|
+
? {
|
|
169
|
+
id: state.currentPlan?.id,
|
|
170
|
+
text: currentPlanText,
|
|
171
|
+
source: state.currentPlan?.source,
|
|
172
|
+
approved: state.currentPlan?.approved,
|
|
173
|
+
sourceMessageIds: state.currentPlan?.sourceMessageIds ?? [],
|
|
174
|
+
}
|
|
175
|
+
: null,
|
|
176
|
+
activeConstraints,
|
|
177
|
+
keyDecisions,
|
|
178
|
+
tasks,
|
|
179
|
+
openQuestions,
|
|
180
|
+
risks: state.risks.map((risk) => sanitizeStateText(risk)).filter((risk): risk is string => Boolean(risk)),
|
|
181
|
+
artifacts,
|
|
182
|
+
agentContributions,
|
|
183
|
+
approvedBy: state.approvedBy ? sanitizeStateText(state.approvedBy) : undefined,
|
|
184
|
+
approvalNote: state.approvalNote ? sanitizeStateText(state.approvalNote) : undefined,
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
104
188
|
function createStableId(prefix: string, ...parts: Array<string | number | undefined>): string {
|
|
105
189
|
const payload = parts
|
|
106
190
|
.map((part) => (part === undefined ? '' : String(part)))
|
|
@@ -488,7 +572,7 @@ export function buildContextCompactionPrompt(params: ContextCompactionPromptPara
|
|
|
488
572
|
params.previousSummary.trim() || 'None',
|
|
489
573
|
'</previous-summary>',
|
|
490
574
|
'<existing-workstream-state>',
|
|
491
|
-
JSON.stringify(params.existingState),
|
|
575
|
+
JSON.stringify(buildExistingWorkstreamStateForCompactionPrompt(params.existingState)),
|
|
492
576
|
'</existing-workstream-state>',
|
|
493
577
|
'<new-messages>',
|
|
494
578
|
params.transcript || 'None',
|
|
@@ -605,27 +689,34 @@ export function createContextCompactionRuntime(
|
|
|
605
689
|
.filter((constraint): constraint is typeof constraint & { text: string } => Boolean(constraint.text))
|
|
606
690
|
|
|
607
691
|
const openQuestions = state.openQuestions
|
|
608
|
-
.map((question) =>
|
|
609
|
-
.filter((question): question is
|
|
692
|
+
.map((question) => sanitizeStateText(question.text))
|
|
693
|
+
.filter((question): question is string => Boolean(question))
|
|
610
694
|
|
|
611
695
|
const decisions = state.keyDecisions
|
|
612
696
|
.map((decision) => ({
|
|
613
|
-
|
|
697
|
+
agent: decision.agent,
|
|
614
698
|
decision: sanitizeStateText(decision.decision),
|
|
615
699
|
rationale: sanitizeStateText(decision.rationale),
|
|
700
|
+
confidence: decision.confidence,
|
|
616
701
|
}))
|
|
617
702
|
.filter((decision): decision is typeof decision & { decision: string; rationale: string } =>
|
|
618
703
|
Boolean(decision.decision && decision.rationale),
|
|
619
704
|
)
|
|
620
705
|
|
|
621
706
|
const tasks = state.tasks
|
|
622
|
-
.map((task) => ({
|
|
707
|
+
.map((task) => ({
|
|
708
|
+
title: sanitizeStateText(task.title),
|
|
709
|
+
status: task.status,
|
|
710
|
+
owner: sanitizeStateText(task.owner),
|
|
711
|
+
externalId: task.externalId,
|
|
712
|
+
source: task.source,
|
|
713
|
+
}))
|
|
623
714
|
.filter((task): task is typeof task & { title: string; owner: string } => Boolean(task.title && task.owner))
|
|
624
715
|
|
|
625
716
|
const artifacts = state.artifacts
|
|
626
717
|
.map((artifact) => ({
|
|
627
|
-
...artifact,
|
|
628
718
|
name: sanitizeStateText(artifact.name),
|
|
719
|
+
type: artifact.type,
|
|
629
720
|
pointer: sanitizeStateText(artifact.pointer),
|
|
630
721
|
}))
|
|
631
722
|
.filter((artifact): artifact is typeof artifact & { name: string; pointer: string } =>
|
|
@@ -633,7 +724,7 @@ export function createContextCompactionRuntime(
|
|
|
633
724
|
)
|
|
634
725
|
|
|
635
726
|
const agentContributions = state.agentContributions
|
|
636
|
-
.map((note) => ({
|
|
727
|
+
.map((note) => ({ agent: note.agent, summary: sanitizeStateText(note.summary) }))
|
|
637
728
|
.filter((note): note is typeof note & { summary: string } => Boolean(note.summary))
|
|
638
729
|
|
|
639
730
|
const payload = {
|
|
@@ -661,12 +752,9 @@ export function createContextCompactionRuntime(
|
|
|
661
752
|
artifacts,
|
|
662
753
|
agentContributions,
|
|
663
754
|
advisory: {
|
|
664
|
-
approvedBy: state.approvedBy
|
|
665
|
-
|
|
666
|
-
approvalMessageId: state.approvalMessageId ?? null,
|
|
667
|
-
approvalNote: state.approvalNote ?? null,
|
|
755
|
+
approvedBy: state.approvedBy ? sanitizeStateText(state.approvedBy) : null,
|
|
756
|
+
approvalNote: state.approvalNote ? sanitizeStateText(state.approvalNote) : null,
|
|
668
757
|
},
|
|
669
|
-
lastUpdated: state.lastUpdated,
|
|
670
758
|
}
|
|
671
759
|
|
|
672
760
|
return ['<workstream-state>', JSON.stringify(payload, null, 2), '</workstream-state>'].join('\n')
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { formatUtcPromptDate } from '../utils/date-time'
|
|
2
|
+
|
|
1
3
|
export function getFactRetrievalMessages(
|
|
2
4
|
parsedMessages: string,
|
|
3
5
|
customPrompt?: string,
|
|
@@ -38,7 +40,7 @@ Hard rules:
|
|
|
38
40
|
- Prefer returning fewer items. If uncertain, return an empty list.
|
|
39
41
|
- Max ${maxFacts} facts.
|
|
40
42
|
|
|
41
|
-
Today's date is ${new Date()
|
|
43
|
+
Today's date is ${formatUtcPromptDate(new Date())}.
|
|
42
44
|
${baseInstructions}`
|
|
43
45
|
|
|
44
46
|
const userPrompt = `Conversation:\n${parsedMessages}`
|
|
@@ -220,8 +220,6 @@ export const LotaRuntimeConfigSchema = z.object({
|
|
|
220
220
|
aiGateway: z.object({
|
|
221
221
|
url: z.string().trim().min(1),
|
|
222
222
|
key: z.string().trim().min(1),
|
|
223
|
-
admin: z.string().trim().min(1).optional(),
|
|
224
|
-
pass: z.string().trim().min(1).optional(),
|
|
225
223
|
embeddingModel: z.string().trim().min(1).default('openai/text-embedding-3-small'),
|
|
226
224
|
}),
|
|
227
225
|
s3: z.object({
|
|
@@ -295,8 +293,6 @@ export const LOTA_RUNTIME_ENV_KEYS = Object.freeze([
|
|
|
295
293
|
'REDIS_URL',
|
|
296
294
|
'AI_GATEWAY_URL',
|
|
297
295
|
'AI_GATEWAY_KEY',
|
|
298
|
-
'AI_GATEWAY_ADMIN',
|
|
299
|
-
'AI_GATEWAY_PASS',
|
|
300
296
|
'AI_EMBEDDING_MODEL',
|
|
301
297
|
'S3_ENDPOINT',
|
|
302
298
|
'S3_BUCKET',
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from '../system-agents/title-generator.agent'
|
|
11
11
|
import { workstreamService } from './workstream.service'
|
|
12
12
|
|
|
13
|
-
const WORKSTREAM_TITLE_TIMEOUT_MS =
|
|
13
|
+
const WORKSTREAM_TITLE_TIMEOUT_MS = 30_000
|
|
14
14
|
|
|
15
15
|
class WorkstreamTitleService {
|
|
16
16
|
helperRuntime = createHelperModelRuntime()
|
|
@@ -632,7 +632,10 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
632
632
|
const workstreamRecord = await waitForWorkstreamCompactionIfNeeded(workstreamRef)
|
|
633
633
|
timer.step('compaction-gate')
|
|
634
634
|
if (toOptionalTrimmedString(workstreamRecord.activeRunId)) {
|
|
635
|
-
|
|
635
|
+
const clearedStaleRun = await workstreamService.clearStaleActiveRunIfMissingFromRegistry(workstreamRef)
|
|
636
|
+
if (!clearedStaleRun) {
|
|
637
|
+
throw new WorkstreamTurnError('A chat run is already active.', 409)
|
|
638
|
+
}
|
|
636
639
|
}
|
|
637
640
|
|
|
638
641
|
if (params.kind === 'approvalContinuation' || params.kind === 'nativeToolApprovalTurn') {
|
|
@@ -525,6 +525,22 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
525
525
|
)
|
|
526
526
|
}
|
|
527
527
|
|
|
528
|
+
async clearStaleActiveRunIfMissingFromRegistry(workstreamId: RecordIdRef): Promise<boolean> {
|
|
529
|
+
const activeRunId = await this.getActiveRunId(workstreamId)
|
|
530
|
+
if (!activeRunId || chatRunRegistry.has(activeRunId)) {
|
|
531
|
+
return false
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const activeStreamId = await this.getActiveStreamId(workstreamId)
|
|
535
|
+
await Promise.all([
|
|
536
|
+
this.clearActiveRunIdIfMatches(workstreamId, activeRunId),
|
|
537
|
+
activeStreamId ? this.clearActiveStreamIdIfMatches(workstreamId, activeStreamId) : Promise.resolve(),
|
|
538
|
+
])
|
|
539
|
+
|
|
540
|
+
serverLogger.warn`Cleared stale workstream run after process restart: workstream=${recordIdToString(ensureRecordId(workstreamId, TABLES.WORKSTREAM), TABLES.WORKSTREAM)} run=${activeRunId}`
|
|
541
|
+
return true
|
|
542
|
+
}
|
|
543
|
+
|
|
528
544
|
async stopActiveRun(workstreamId: RecordIdRef): Promise<boolean> {
|
|
529
545
|
const activeRunId = await this.getActiveRunId(workstreamId)
|
|
530
546
|
if (!activeRunId) return false
|
|
@@ -534,7 +550,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
534
550
|
return true
|
|
535
551
|
}
|
|
536
552
|
|
|
537
|
-
await this.
|
|
553
|
+
await this.clearStaleActiveRunIfMissingFromRegistry(workstreamId)
|
|
538
554
|
return false
|
|
539
555
|
}
|
|
540
556
|
|
|
@@ -675,6 +691,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
675
691
|
typeof workstream.activeRunId === 'string' && workstream.activeRunId.trim().length > 0
|
|
676
692
|
? workstream.activeRunId
|
|
677
693
|
: null
|
|
694
|
+
const isRunning = activeRunId !== null && chatRunRegistry.has(activeRunId)
|
|
678
695
|
const isCompacting = workstream.isCompacting === true
|
|
679
696
|
const mode = workstream.mode
|
|
680
697
|
const core = workstream.core
|
|
@@ -688,7 +705,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
688
705
|
core,
|
|
689
706
|
...(coreType ? { coreType } : {}),
|
|
690
707
|
nameGenerated: workstream.nameGenerated,
|
|
691
|
-
isRunning
|
|
708
|
+
isRunning,
|
|
692
709
|
isCompacting,
|
|
693
710
|
...(isAgentName(workstream.agentId) ? { agentId: workstream.agentId } : {}),
|
|
694
711
|
title: workstream.title ?? this.getDefaultTitle(workstream),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ToolLoopAgent } from 'ai'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
|
|
4
|
+
import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
|
|
5
5
|
import {
|
|
6
6
|
OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
|
|
7
7
|
OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
@@ -40,8 +40,8 @@ Return valid data for:
|
|
|
40
40
|
export function createContextCompactionAgent(options: CreateHelperToolLoopAgentOptions) {
|
|
41
41
|
return new ToolLoopAgent({
|
|
42
42
|
id: 'context-compaction',
|
|
43
|
-
model:
|
|
44
|
-
headers:
|
|
43
|
+
model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
|
|
44
|
+
headers: buildAiGatewayDirectCacheHeaders('context-compaction'),
|
|
45
45
|
providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
46
46
|
...resolveHelperAgentOptions(options, { instructions: CONTEXT_COMPACTION_PROMPT }),
|
|
47
47
|
})
|
|
@@ -4,6 +4,7 @@ import { z } from 'zod'
|
|
|
4
4
|
|
|
5
5
|
import type { ToolDefinition } from '../ai/definitions'
|
|
6
6
|
import { aiLogger } from '../config/logger'
|
|
7
|
+
import { formatUtcPromptDate } from '../utils/date-time'
|
|
7
8
|
import { isRecord } from '../utils/string'
|
|
8
9
|
import { assertSubstantiveAgentResult } from './agent-result'
|
|
9
10
|
|
|
@@ -32,15 +33,7 @@ function resolveAgentModel(model: AgentModel): LanguageModel {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
function buildCurrentDateContext(now = new Date()): string {
|
|
35
|
-
|
|
36
|
-
const humanDate = new Intl.DateTimeFormat('en-US', {
|
|
37
|
-
timeZone: 'UTC',
|
|
38
|
-
year: 'numeric',
|
|
39
|
-
month: 'long',
|
|
40
|
-
day: 'numeric',
|
|
41
|
-
}).format(now)
|
|
42
|
-
|
|
43
|
-
return [`Today is ${isoDate} (${humanDate}, UTC).`, 'Use this exact date for any recency reasoning.'].join(' ')
|
|
36
|
+
return [`Today is ${formatUtcPromptDate(now)}.`, 'Use this exact date for any recency reasoning.'].join(' ')
|
|
44
37
|
}
|
|
45
38
|
|
|
46
39
|
export function buildRecencyInstructions(tools?: ToolSet): string {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ToolLoopAgent } from 'ai'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
|
|
4
|
+
import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
|
|
5
5
|
import {
|
|
6
6
|
OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS,
|
|
7
7
|
OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
|
|
@@ -32,8 +32,8 @@ Set every item.relevance as a string; use empty string when no reason is needed.
|
|
|
32
32
|
export function createMemoryRerankerAgent(options: CreateHelperToolLoopAgentOptions) {
|
|
33
33
|
return new ToolLoopAgent({
|
|
34
34
|
id: 'memory-reranker',
|
|
35
|
-
model:
|
|
36
|
-
headers:
|
|
35
|
+
model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
|
|
36
|
+
headers: buildAiGatewayDirectCacheHeaders('memory-reranker'),
|
|
37
37
|
providerOptions: OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS,
|
|
38
38
|
...resolveHelperAgentOptions(options),
|
|
39
39
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ToolLoopAgent } from 'ai'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
|
|
4
|
+
import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
|
|
5
5
|
import {
|
|
6
6
|
OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
|
|
7
7
|
OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
@@ -52,8 +52,8 @@ Return only the schema fields with no extra formatting.
|
|
|
52
52
|
export function createOrgMemoryAgent(options: CreateHelperToolLoopAgentOptions) {
|
|
53
53
|
return new ToolLoopAgent({
|
|
54
54
|
id: 'org-memory',
|
|
55
|
-
model:
|
|
56
|
-
headers:
|
|
55
|
+
model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
|
|
56
|
+
headers: buildAiGatewayDirectCacheHeaders('org-memory'),
|
|
57
57
|
providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
58
58
|
...resolveHelperAgentOptions(options),
|
|
59
59
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ToolLoopAgent } from 'ai'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { aiGatewayModel } from '../ai-gateway/ai-gateway'
|
|
4
|
+
import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
|
|
5
5
|
import { getLeadAgentDisplayName } from '../config/agent-defaults'
|
|
6
6
|
import {
|
|
7
7
|
OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
|
|
@@ -79,8 +79,8 @@ Return only the title text. No quotes, labels, JSON, markdown, or explanation.
|
|
|
79
79
|
export function createRecentActivityTitleRefinerAgent(options: CreateHelperToolLoopAgentOptions) {
|
|
80
80
|
return new ToolLoopAgent({
|
|
81
81
|
id: 'recent-activity-title-refiner',
|
|
82
|
-
model:
|
|
83
|
-
headers:
|
|
82
|
+
model: aiGatewayModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
|
|
83
|
+
headers: buildAiGatewayDirectCacheHeaders('recent-activity-title-refiner'),
|
|
84
84
|
providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
85
85
|
...resolveHelperAgentOptions(options, {
|
|
86
86
|
instructions: buildRecentActivityTitleRefinerPrompt(),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ToolLoopAgent } from 'ai'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
|
|
4
|
+
import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
|
|
5
5
|
import {
|
|
6
6
|
OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
|
|
7
7
|
OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
@@ -66,8 +66,8 @@ Return only schema fields.
|
|
|
66
66
|
export function createRegularChatMemoryDigestAgent(options: CreateHelperToolLoopAgentOptions) {
|
|
67
67
|
return new ToolLoopAgent({
|
|
68
68
|
id: 'regular-chat-memory-digest',
|
|
69
|
-
model:
|
|
70
|
-
headers:
|
|
69
|
+
model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
|
|
70
|
+
headers: buildAiGatewayDirectCacheHeaders('regular-chat-memory-digest'),
|
|
71
71
|
providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
72
72
|
...resolveHelperAgentOptions(options, {
|
|
73
73
|
instructions: regularChatMemoryDigestPrompt,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ToolLoopAgent } from 'ai'
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
|
|
5
|
+
import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
|
|
6
6
|
import {
|
|
7
7
|
OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
|
|
8
8
|
OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
@@ -79,8 +79,8 @@ export type SkillCandidate = z.infer<typeof SkillCandidateSchema>
|
|
|
79
79
|
export function createSkillExtractorAgent(options: CreateHelperToolLoopAgentOptions) {
|
|
80
80
|
return new ToolLoopAgent({
|
|
81
81
|
id: 'skill-extractor',
|
|
82
|
-
model:
|
|
83
|
-
headers:
|
|
82
|
+
model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
|
|
83
|
+
headers: buildAiGatewayDirectCacheHeaders('skill-extractor'),
|
|
84
84
|
providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
85
85
|
...resolveHelperAgentOptions(options, {
|
|
86
86
|
instructions: skillExtractorPrompt,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ToolLoopAgent } from 'ai'
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
|
|
5
|
+
import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
|
|
6
6
|
import {
|
|
7
7
|
OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
|
|
8
8
|
OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
@@ -69,8 +69,8 @@ export const SkillManagerOutputSchema = z.object({
|
|
|
69
69
|
export function createSkillManagerAgent(options: CreateHelperToolLoopAgentOptions) {
|
|
70
70
|
return new ToolLoopAgent({
|
|
71
71
|
id: 'skill-manager',
|
|
72
|
-
model:
|
|
73
|
-
headers:
|
|
72
|
+
model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
|
|
73
|
+
headers: buildAiGatewayDirectCacheHeaders('skill-manager'),
|
|
74
74
|
providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
75
75
|
...resolveHelperAgentOptions(options, {
|
|
76
76
|
instructions: skillManagerPrompt,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ToolLoopAgent } from 'ai'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { aiGatewayModel } from '../ai-gateway/ai-gateway'
|
|
4
|
+
import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
|
|
5
5
|
import {
|
|
6
6
|
OPENROUTER_FAST_REASONING_MODEL_ID,
|
|
7
7
|
OPENROUTER_MINIMAL_REASONING_PROVIDER_OPTIONS,
|
|
@@ -33,8 +33,8 @@ Return only the title text. No quotes, no labels, no explanation.
|
|
|
33
33
|
export function createWorkstreamTitleGeneratorAgent(options: CreateHelperToolLoopAgentOptions) {
|
|
34
34
|
return new ToolLoopAgent({
|
|
35
35
|
id: 'workstream-title-generator',
|
|
36
|
-
model:
|
|
37
|
-
headers:
|
|
36
|
+
model: aiGatewayModel(OPENROUTER_FAST_REASONING_MODEL_ID),
|
|
37
|
+
headers: buildAiGatewayDirectCacheHeaders('workstream-title-generator'),
|
|
38
38
|
providerOptions: OPENROUTER_MINIMAL_REASONING_PROVIDER_OPTIONS,
|
|
39
39
|
...resolveHelperAgentOptions(options, {
|
|
40
40
|
instructions: WORKSTREAM_TITLE_GENERATOR_PROMPT,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
|
|
2
|
+
import { buildAiGatewayStrictSemanticCacheHeaders } from '../ai-gateway/cache-headers'
|
|
3
3
|
import {
|
|
4
4
|
OPENROUTER_MEDIUM_REASONING_PROVIDER_OPTIONS,
|
|
5
5
|
OPENROUTER_WEB_RESEARCH_MODEL_ID,
|
|
@@ -13,9 +13,9 @@ export const researchTopicTool = createDelegatedAgentTool({
|
|
|
13
13
|
id: 'researchTopic',
|
|
14
14
|
description:
|
|
15
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.',
|
|
16
|
-
model: () =>
|
|
16
|
+
model: () => aiGatewayChatModel(OPENROUTER_WEB_RESEARCH_MODEL_ID),
|
|
17
17
|
providerOptions: OPENROUTER_MEDIUM_REASONING_PROVIDER_OPTIONS,
|
|
18
|
-
headers:
|
|
18
|
+
headers: buildAiGatewayStrictSemanticCacheHeaders('researchTopic'),
|
|
19
19
|
instructions: RESEARCHER_PROMPT,
|
|
20
20
|
tools: { searchWeb: searchWebTool.create(), fetchWebpage: fetchWebpageTool.create() },
|
|
21
21
|
})
|
package/src/utils/date-time.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
export { toIsoDateTimeString, toOptionalIsoDateTimeString } from '@lota-sdk/shared'
|
|
2
2
|
|
|
3
|
+
const PROMPT_DATE_FORMATTER = new Intl.DateTimeFormat('en-US', {
|
|
4
|
+
timeZone: 'UTC',
|
|
5
|
+
year: 'numeric',
|
|
6
|
+
month: 'long',
|
|
7
|
+
day: 'numeric',
|
|
8
|
+
})
|
|
9
|
+
|
|
3
10
|
export function toDatabaseDateTime(value: string | Date | null | undefined): Date | undefined {
|
|
4
11
|
if (value === null || value === undefined) return undefined
|
|
5
12
|
return value instanceof Date ? value : new Date(value)
|
|
6
13
|
}
|
|
14
|
+
|
|
15
|
+
export function formatUtcPromptDate(value: Date): string {
|
|
16
|
+
return PROMPT_DATE_FORMATTER.format(value)
|
|
17
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CHARS_PER_TOKEN_ESTIMATE } from '../../utils/string'
|
|
2
2
|
|
|
3
|
-
export const DEFAULT_FILE_SECTION_CHUNK_MAX_CHARS =
|
|
3
|
+
export const DEFAULT_FILE_SECTION_CHUNK_MAX_CHARS = 1_500_000
|
|
4
4
|
export const MIN_FILE_SECTION_CHUNK_MAX_CHARS = 4_000
|
|
5
5
|
export const DEFAULT_FILE_SECTION_CHUNK_MIN_CHARS = 10_000
|
|
6
6
|
const SECTION_SEPARATOR_LENGTH = 2
|