@lota-sdk/core 0.1.5
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/infrastructure/schema/00_workstream.surql +55 -0
- package/infrastructure/schema/01_memory.surql +47 -0
- package/infrastructure/schema/02_execution_plan.surql +62 -0
- package/infrastructure/schema/03_learned_skill.surql +32 -0
- package/infrastructure/schema/04_runtime_bootstrap.surql +8 -0
- package/package.json +128 -0
- package/src/ai/definitions.ts +308 -0
- package/src/bifrost/bifrost.ts +256 -0
- package/src/config/agent-defaults.ts +99 -0
- package/src/config/constants.ts +33 -0
- package/src/config/env-shapes.ts +122 -0
- package/src/config/logger.ts +29 -0
- package/src/config/model-constants.ts +31 -0
- package/src/config/search.ts +17 -0
- package/src/config/workstream-defaults.ts +68 -0
- package/src/db/base.service.ts +55 -0
- package/src/db/cursor-pagination.ts +73 -0
- package/src/db/memory-query-builder.ts +207 -0
- package/src/db/memory-store.helpers.ts +118 -0
- package/src/db/memory-store.rows.ts +29 -0
- package/src/db/memory-store.ts +974 -0
- package/src/db/memory-types.ts +193 -0
- package/src/db/memory.ts +505 -0
- package/src/db/record-id.ts +78 -0
- package/src/db/service.ts +932 -0
- package/src/db/startup.ts +152 -0
- package/src/db/tables.ts +20 -0
- package/src/document/org-document-chunking.ts +224 -0
- package/src/document/parsing.ts +40 -0
- package/src/embeddings/provider.ts +76 -0
- package/src/index.ts +302 -0
- package/src/queues/context-compaction.queue.ts +82 -0
- package/src/queues/document-processor.queue.ts +118 -0
- package/src/queues/memory-consolidation.queue.ts +65 -0
- package/src/queues/post-chat-memory.queue.ts +128 -0
- package/src/queues/recent-activity-title-refinement.queue.ts +69 -0
- package/src/queues/regular-chat-memory-digest.config.ts +12 -0
- package/src/queues/regular-chat-memory-digest.queue.ts +73 -0
- package/src/queues/skill-extraction.config.ts +9 -0
- package/src/queues/skill-extraction.queue.ts +62 -0
- package/src/redis/connection.ts +176 -0
- package/src/redis/index.ts +30 -0
- package/src/redis/org-memory-lock.ts +43 -0
- package/src/redis/redis-lease-lock.ts +158 -0
- package/src/runtime/agent-contract.ts +1 -0
- package/src/runtime/agent-prompt-context.ts +119 -0
- package/src/runtime/agent-runtime-policy.ts +192 -0
- package/src/runtime/agent-stream-helpers.ts +117 -0
- package/src/runtime/agent-types.ts +22 -0
- package/src/runtime/approval-continuation.ts +16 -0
- package/src/runtime/chat-attachments.ts +46 -0
- package/src/runtime/chat-message.ts +10 -0
- package/src/runtime/chat-request-routing.ts +21 -0
- package/src/runtime/chat-run-orchestration.ts +25 -0
- package/src/runtime/chat-run-registry.ts +20 -0
- package/src/runtime/chat-types.ts +18 -0
- package/src/runtime/context-compaction-constants.ts +11 -0
- package/src/runtime/context-compaction-runtime.ts +86 -0
- package/src/runtime/context-compaction.ts +909 -0
- package/src/runtime/execution-plan.ts +59 -0
- package/src/runtime/helper-model.ts +405 -0
- package/src/runtime/indexed-repositories-policy.ts +28 -0
- package/src/runtime/instruction-sections.ts +8 -0
- package/src/runtime/llm-content.ts +71 -0
- package/src/runtime/memory-block.ts +264 -0
- package/src/runtime/memory-digest-policy.ts +14 -0
- package/src/runtime/memory-format.ts +8 -0
- package/src/runtime/memory-pipeline.ts +570 -0
- package/src/runtime/memory-prompts-fact.ts +47 -0
- package/src/runtime/memory-prompts-parse.ts +3 -0
- package/src/runtime/memory-prompts-update.ts +37 -0
- package/src/runtime/memory-scope.ts +43 -0
- package/src/runtime/plugin-types.ts +10 -0
- package/src/runtime/retrieval-adapters.ts +25 -0
- package/src/runtime/retrieval-pipeline.ts +3 -0
- package/src/runtime/runtime-extensions.ts +154 -0
- package/src/runtime/skill-extraction-policy.ts +3 -0
- package/src/runtime/team-consultation-orchestrator.ts +245 -0
- package/src/runtime/team-consultation-prompts.ts +32 -0
- package/src/runtime/title-helpers.ts +12 -0
- package/src/runtime/turn-lifecycle.ts +28 -0
- package/src/runtime/workstream-chat-helpers.ts +187 -0
- package/src/runtime/workstream-routing-policy.ts +301 -0
- package/src/runtime/workstream-state.ts +261 -0
- package/src/services/attachment.service.ts +159 -0
- package/src/services/chat-attachments.service.ts +17 -0
- package/src/services/chat-run-registry.service.ts +3 -0
- package/src/services/context-compaction-runtime.ts +13 -0
- package/src/services/context-compaction.service.ts +115 -0
- package/src/services/document-chunk.service.ts +141 -0
- package/src/services/execution-plan.service.ts +890 -0
- package/src/services/learned-skill.service.ts +328 -0
- package/src/services/memory-assessment.service.ts +43 -0
- package/src/services/memory.service.ts +807 -0
- package/src/services/memory.utils.ts +84 -0
- package/src/services/mutating-approval.service.ts +110 -0
- package/src/services/recent-activity-title.service.ts +74 -0
- package/src/services/recent-activity.service.ts +397 -0
- package/src/services/workstream-change-tracker.service.ts +313 -0
- package/src/services/workstream-message.service.ts +283 -0
- package/src/services/workstream-title.service.ts +58 -0
- package/src/services/workstream-turn-preparation.ts +1340 -0
- package/src/services/workstream-turn.ts +37 -0
- package/src/services/workstream.service.ts +854 -0
- package/src/services/workstream.types.ts +118 -0
- package/src/storage/attachment-parser.ts +101 -0
- package/src/storage/attachment-storage.service.ts +391 -0
- package/src/storage/attachments.types.ts +11 -0
- package/src/storage/attachments.utils.ts +58 -0
- package/src/storage/generated-document-storage.service.ts +55 -0
- package/src/system-agents/agent-result.ts +27 -0
- package/src/system-agents/context-compacter.agent.ts +46 -0
- package/src/system-agents/delegated-agent-factory.ts +177 -0
- package/src/system-agents/helper-agent-options.ts +20 -0
- package/src/system-agents/memory-reranker.agent.ts +38 -0
- package/src/system-agents/memory.agent.ts +58 -0
- package/src/system-agents/recent-activity-title-refiner.agent.ts +53 -0
- package/src/system-agents/regular-chat-memory-digest.agent.ts +75 -0
- package/src/system-agents/researcher.agent.ts +34 -0
- package/src/system-agents/skill-extractor.agent.ts +88 -0
- package/src/system-agents/skill-manager.agent.ts +80 -0
- package/src/system-agents/title-generator.agent.ts +42 -0
- package/src/system-agents/workstream-tracker.agent.ts +58 -0
- package/src/tools/execution-plan.tool.ts +163 -0
- package/src/tools/fetch-webpage.tool.ts +132 -0
- package/src/tools/firecrawl-client.ts +12 -0
- package/src/tools/memory-block.tool.ts +55 -0
- package/src/tools/read-file-parts.tool.ts +80 -0
- package/src/tools/remember-memory.tool.ts +85 -0
- package/src/tools/research-topic.tool.ts +15 -0
- package/src/tools/search-tools.ts +55 -0
- package/src/tools/search-web.tool.ts +175 -0
- package/src/tools/team-think.tool.ts +125 -0
- package/src/tools/tool-contract.ts +21 -0
- package/src/tools/user-questions.tool.ts +18 -0
- package/src/utils/async.ts +50 -0
- package/src/utils/date-time.ts +34 -0
- package/src/utils/error.ts +10 -0
- package/src/utils/errors.ts +28 -0
- package/src/utils/hono-error-handler.ts +71 -0
- package/src/utils/string.ts +51 -0
- package/src/workers/bootstrap.ts +44 -0
- package/src/workers/memory-consolidation.worker.ts +318 -0
- package/src/workers/regular-chat-memory-digest.helpers.ts +100 -0
- package/src/workers/regular-chat-memory-digest.runner.ts +363 -0
- package/src/workers/regular-chat-memory-digest.worker.ts +22 -0
- package/src/workers/skill-extraction.runner.ts +331 -0
- package/src/workers/skill-extraction.worker.ts +22 -0
- package/src/workers/utils/repo-indexer-chunker.ts +331 -0
- package/src/workers/utils/repo-structure-extractor.ts +645 -0
- package/src/workers/utils/repomix-process-concurrency.ts +65 -0
- package/src/workers/utils/sandbox-error.ts +5 -0
- package/src/workers/worker-utils.ts +182 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { S3Client } from 'bun'
|
|
2
|
+
|
|
3
|
+
import { env } from '../config/env-shapes'
|
|
4
|
+
|
|
5
|
+
function toSafeSegment(value: string): string {
|
|
6
|
+
return value
|
|
7
|
+
.trim()
|
|
8
|
+
.replace(/\\/g, '/')
|
|
9
|
+
.replace(/^\/+|\/+$/g, '')
|
|
10
|
+
.replace(/\.\.+/g, '.')
|
|
11
|
+
.replace(/[^a-zA-Z0-9._/-]+/g, '-')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function buildGeneratedDocumentStorageKey(params: {
|
|
15
|
+
organizationId: string
|
|
16
|
+
namespace: string
|
|
17
|
+
relativePath: string
|
|
18
|
+
}): string {
|
|
19
|
+
const safeOrganizationId = toSafeSegment(params.organizationId)
|
|
20
|
+
const safeNamespace = toSafeSegment(params.namespace)
|
|
21
|
+
const safeRelativePath = toSafeSegment(params.relativePath)
|
|
22
|
+
return `${safeOrganizationId}/generated/${safeNamespace}/${safeRelativePath}`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class GeneratedDocumentStorageService {
|
|
26
|
+
private _client: InstanceType<typeof S3Client> | undefined
|
|
27
|
+
|
|
28
|
+
private get client(): InstanceType<typeof S3Client> {
|
|
29
|
+
if (!this._client) {
|
|
30
|
+
this._client = new S3Client({
|
|
31
|
+
accessKeyId: env.S3_ACCESS_KEY_ID,
|
|
32
|
+
secretAccessKey: env.S3_SECRET_ACCESS_KEY,
|
|
33
|
+
bucket: env.S3_BUCKET,
|
|
34
|
+
endpoint: env.S3_ENDPOINT,
|
|
35
|
+
region: env.S3_REGION,
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
return this._client
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async writeTextArtifact(params: {
|
|
42
|
+
organizationId: string
|
|
43
|
+
namespace: string
|
|
44
|
+
relativePath: string
|
|
45
|
+
content: string
|
|
46
|
+
mediaType: string
|
|
47
|
+
}): Promise<{ storageKey: string; sizeBytes: number }> {
|
|
48
|
+
const storageKey = buildGeneratedDocumentStorageKey(params)
|
|
49
|
+
const sizeBytes = Buffer.byteLength(params.content, 'utf8')
|
|
50
|
+
await this.client.file(storageKey).write(params.content, { type: params.mediaType })
|
|
51
|
+
return { storageKey, sizeBytes }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const generatedDocumentStorageService = new GeneratedDocumentStorageService()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const SUBSTANTIVE_CHAR_PATTERN = /[\p{L}\p{N}]/u
|
|
2
|
+
const WORD_LIKE_PATTERN = /[\p{L}\p{N}][\p{L}\p{N}/._'-]*/gu
|
|
3
|
+
|
|
4
|
+
function normalizeAgentResultText(value: string): string {
|
|
5
|
+
return value.replaceAll(String.fromCharCode(0), '').replace(/\r/g, '').trim()
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function isSubstantiveAgentResult(value: string): boolean {
|
|
9
|
+
const normalized = normalizeAgentResultText(value)
|
|
10
|
+
if (!normalized || !SUBSTANTIVE_CHAR_PATTERN.test(normalized)) {
|
|
11
|
+
return false
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const wordLikeParts = normalized.match(WORD_LIKE_PATTERN) ?? []
|
|
15
|
+
const alphaNumericCount = Array.from(normalized.matchAll(/[\p{L}\p{N}]/gu)).length
|
|
16
|
+
|
|
17
|
+
return alphaNumericCount >= 4 && (normalized.length >= 8 || wordLikeParts.length >= 2)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function assertSubstantiveAgentResult(value: string, label = 'Agent result'): string {
|
|
21
|
+
const normalized = normalizeAgentResultText(value)
|
|
22
|
+
if (!isSubstantiveAgentResult(normalized)) {
|
|
23
|
+
throw new Error(`${label} must contain substantive text.`)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return normalized
|
|
27
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ToolLoopAgent } from 'ai'
|
|
2
|
+
|
|
3
|
+
import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
|
|
4
|
+
import {
|
|
5
|
+
OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
|
|
6
|
+
OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
7
|
+
} from '../config/model-constants'
|
|
8
|
+
import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
|
|
9
|
+
import { resolveHelperAgentOptions } from './helper-agent-options'
|
|
10
|
+
|
|
11
|
+
const contextCompacterPrompt = `<agent-instructions>
|
|
12
|
+
You are a **Context Compacter** that produces both:
|
|
13
|
+
1) a dense but shorter summary of prior context
|
|
14
|
+
2) a structured state delta for durable execution continuity
|
|
15
|
+
|
|
16
|
+
<task>
|
|
17
|
+
Compress historical context while preserving execution-critical details.
|
|
18
|
+
When provided with existing state, update only changed fields.
|
|
19
|
+
</task>
|
|
20
|
+
|
|
21
|
+
<rules>
|
|
22
|
+
- Preserve concrete decisions, constraints, assumptions, plans, open questions, and unresolved risks.
|
|
23
|
+
- Preserve action ownership, specialist consultations, and the latest working direction.
|
|
24
|
+
- Preserve provenance by attaching \`sourceMessageIds\` only from the provided message IDs.
|
|
25
|
+
- Keep identifiers, numbers, dates, and proper nouns when present.
|
|
26
|
+
- Remove filler and repetition.
|
|
27
|
+
- Do not invent details.
|
|
28
|
+
- If uncertain, prefer an empty array or null over guessing.
|
|
29
|
+
</rules>
|
|
30
|
+
|
|
31
|
+
<output-format>
|
|
32
|
+
The caller enforces a structured output schema.
|
|
33
|
+
Return valid data for:
|
|
34
|
+
- summary: concise text summary
|
|
35
|
+
- stateDelta: include every field; use empty arrays for unchanged list fields, null for unchanged nullable fields, and \`currentPlan.action\` to signal \`unchanged\`, \`clear\`, or \`set\`
|
|
36
|
+
</output-format>
|
|
37
|
+
</agent-instructions>`
|
|
38
|
+
|
|
39
|
+
export function createContextCompacterAgent(options: CreateHelperToolLoopAgentOptions) {
|
|
40
|
+
return new ToolLoopAgent({
|
|
41
|
+
id: 'context-compacter',
|
|
42
|
+
model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
|
|
43
|
+
providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
44
|
+
...resolveHelperAgentOptions(options, { instructions: contextCompacterPrompt }),
|
|
45
|
+
})
|
|
46
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { ToolLoopAgent, stepCountIs, tool } from 'ai'
|
|
2
|
+
import type { ModelMessage, LanguageModel, ToolLoopAgentSettings, ToolSet } from 'ai'
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
|
|
5
|
+
import type { ToolDefinition } from '../ai/definitions'
|
|
6
|
+
import { isRecord } from '../utils/string'
|
|
7
|
+
import { assertSubstantiveAgentResult } from './agent-result'
|
|
8
|
+
|
|
9
|
+
type AgentProviderOptions = ToolLoopAgentSettings['providerOptions']
|
|
10
|
+
|
|
11
|
+
interface DelegatedAgentDefinition {
|
|
12
|
+
id: string
|
|
13
|
+
description: string
|
|
14
|
+
model: LanguageModel
|
|
15
|
+
providerOptions?: AgentProviderOptions
|
|
16
|
+
instructions: string
|
|
17
|
+
tools?: ToolSet
|
|
18
|
+
maxSteps?: number
|
|
19
|
+
maxOutputTokens?: number
|
|
20
|
+
temperature?: number
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface DelegatedAgentDefinitionWithContext<TContext> extends Omit<DelegatedAgentDefinition, 'tools'> {
|
|
24
|
+
createTools: (context: TContext) => ToolSet
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function buildCurrentDateContext(now = new Date()): string {
|
|
28
|
+
const isoDate = now.toISOString().slice(0, 10)
|
|
29
|
+
const humanDate = new Intl.DateTimeFormat('en-US', {
|
|
30
|
+
timeZone: 'UTC',
|
|
31
|
+
year: 'numeric',
|
|
32
|
+
month: 'long',
|
|
33
|
+
day: 'numeric',
|
|
34
|
+
}).format(now)
|
|
35
|
+
|
|
36
|
+
return [`Today is ${isoDate} (${humanDate}, UTC).`, 'Use this exact date for any recency reasoning.'].join(' ')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function buildRecencyInstructions(tools?: ToolSet): string {
|
|
40
|
+
const hasSearchWeb = Boolean(tools && 'searchWeb' in tools)
|
|
41
|
+
|
|
42
|
+
const lines = [
|
|
43
|
+
'<time-awareness>',
|
|
44
|
+
buildCurrentDateContext(),
|
|
45
|
+
'Never assume your model knowledge is current for changing facts, benchmarks, prices, people, product details, regulations, or news.',
|
|
46
|
+
'Prefer exact publication and event dates over vague phrases like "recently" or "currently".',
|
|
47
|
+
'Treat evidence older than 12 months as potentially stale unless the topic is clearly stable.',
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
if (hasSearchWeb) {
|
|
51
|
+
lines.push(
|
|
52
|
+
'If the task depends on current or recent external information, you must verify it with the available web tools before concluding.',
|
|
53
|
+
'When using searchWeb for time-sensitive topics, apply a tbs filter that matches the question (`qdr:d`, `qdr:w`, `qdr:m`, `qdr:y`, or a custom `cdr:1,cd_min:MM/DD/YYYY,cd_max:MM/DD/YYYY` range). Start narrow, then widen only if needed.',
|
|
54
|
+
'If the newest trustworthy evidence is still stale, say that explicitly and explain the limitation instead of presenting it as current.',
|
|
55
|
+
)
|
|
56
|
+
} else {
|
|
57
|
+
lines.push(
|
|
58
|
+
'If the task needs fresh external verification and you do not have web tools, say that current verification is unavailable and separate verified inputs from assumptions.',
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
lines.push('</time-awareness>')
|
|
63
|
+
|
|
64
|
+
return lines.join('\n')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function buildDelegatedAgentInstructions(baseInstructions: string, tools?: ToolSet): string {
|
|
68
|
+
return [baseInstructions.trim(), buildRecencyInstructions(tools), TERMINATION_SUFFIX].join('\n\n')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const TERMINATION_SUFFIX = `
|
|
72
|
+
|
|
73
|
+
<termination>
|
|
74
|
+
When your analysis is complete, return your final answer directly as markdown text. Do not leave the task unfinished or end on a tool result without a final response.
|
|
75
|
+
</termination>`
|
|
76
|
+
|
|
77
|
+
const MAX_RETAINED_AGENT_MESSAGES = 10
|
|
78
|
+
|
|
79
|
+
export function retainCriticalAgentMessages(messages: ModelMessage[]): ModelMessage[] {
|
|
80
|
+
const systemMessages = messages.filter((message) => message.role === 'system')
|
|
81
|
+
const nonSystemMessages = messages.filter((message) => message.role !== 'system')
|
|
82
|
+
|
|
83
|
+
if (nonSystemMessages.length <= MAX_RETAINED_AGENT_MESSAGES) {
|
|
84
|
+
return messages
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const firstUserMessage = nonSystemMessages.find((message) => message.role === 'user')
|
|
88
|
+
const tailMessages = nonSystemMessages.slice(-MAX_RETAINED_AGENT_MESSAGES)
|
|
89
|
+
|
|
90
|
+
if (firstUserMessage && !tailMessages.includes(firstUserMessage)) {
|
|
91
|
+
return [...systemMessages, firstUserMessage, ...tailMessages]
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return [...systemMessages, ...tailMessages]
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function resolveDelegatedAgentTemperature(definition: {
|
|
98
|
+
providerOptions?: AgentProviderOptions
|
|
99
|
+
temperature?: number
|
|
100
|
+
}): number | undefined {
|
|
101
|
+
if (isRecord(definition.providerOptions) && isRecord(definition.providerOptions.openai)) {
|
|
102
|
+
const openaiOptions = definition.providerOptions.openai
|
|
103
|
+
if (
|
|
104
|
+
openaiOptions.forceReasoning === true ||
|
|
105
|
+
(typeof openaiOptions.reasoningEffort === 'string' && openaiOptions.reasoningEffort !== 'none') ||
|
|
106
|
+
(typeof openaiOptions.reasoningSummary === 'string' && openaiOptions.reasoningSummary.length > 0)
|
|
107
|
+
) {
|
|
108
|
+
return undefined
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return definition.temperature ?? 0.2
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function createDelegatedAgentTool(definition: DelegatedAgentDefinition): ToolDefinition<void> {
|
|
116
|
+
const maxSteps = definition.maxSteps ?? 10
|
|
117
|
+
const temperature = resolveDelegatedAgentTemperature(definition)
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
name: definition.id,
|
|
121
|
+
create: () =>
|
|
122
|
+
tool({
|
|
123
|
+
description: definition.description,
|
|
124
|
+
inputSchema: z.object({ task: z.string().min(1) }),
|
|
125
|
+
execute: async ({ task }: { task: string }, { abortSignal }) => {
|
|
126
|
+
const agentTools = definition.tools
|
|
127
|
+
const agent = new ToolLoopAgent({
|
|
128
|
+
id: definition.id,
|
|
129
|
+
model: definition.model,
|
|
130
|
+
...(definition.providerOptions ? { providerOptions: definition.providerOptions } : {}),
|
|
131
|
+
instructions: buildDelegatedAgentInstructions(definition.instructions, agentTools),
|
|
132
|
+
tools: agentTools,
|
|
133
|
+
maxOutputTokens: definition.maxOutputTokens ?? 4096,
|
|
134
|
+
...(typeof temperature === 'number' ? { temperature } : {}),
|
|
135
|
+
stopWhen: [stepCountIs(maxSteps)],
|
|
136
|
+
prepareStep: async ({ messages }) => ({ messages: retainCriticalAgentMessages(messages) }),
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
const result = await agent.generate({ prompt: task, abortSignal })
|
|
140
|
+
return { task, result: assertSubstantiveAgentResult(result.text, `${definition.id} result`) }
|
|
141
|
+
},
|
|
142
|
+
}),
|
|
143
|
+
} as const satisfies ToolDefinition<void>
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function createDelegatedAgentToolWithContext<TContext>(
|
|
147
|
+
definition: DelegatedAgentDefinitionWithContext<TContext>,
|
|
148
|
+
): ToolDefinition<TContext> {
|
|
149
|
+
const maxSteps = definition.maxSteps ?? 10
|
|
150
|
+
const temperature = resolveDelegatedAgentTemperature(definition)
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
name: definition.id,
|
|
154
|
+
create: (context: TContext) =>
|
|
155
|
+
tool({
|
|
156
|
+
description: definition.description,
|
|
157
|
+
inputSchema: z.object({ task: z.string().min(1) }),
|
|
158
|
+
execute: async ({ task }: { task: string }, { abortSignal }) => {
|
|
159
|
+
const agentTools = definition.createTools(context)
|
|
160
|
+
const agent = new ToolLoopAgent({
|
|
161
|
+
id: definition.id,
|
|
162
|
+
model: definition.model,
|
|
163
|
+
...(definition.providerOptions ? { providerOptions: definition.providerOptions } : {}),
|
|
164
|
+
instructions: buildDelegatedAgentInstructions(definition.instructions, agentTools),
|
|
165
|
+
tools: agentTools,
|
|
166
|
+
maxOutputTokens: definition.maxOutputTokens ?? 4096,
|
|
167
|
+
...(typeof temperature === 'number' ? { temperature } : {}),
|
|
168
|
+
stopWhen: [stepCountIs(maxSteps)],
|
|
169
|
+
prepareStep: async ({ messages }) => ({ messages: retainCriticalAgentMessages(messages) }),
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
const result = await agent.generate({ prompt: task, abortSignal })
|
|
173
|
+
return { task, result: assertSubstantiveAgentResult(result.text, `${definition.id} result`) }
|
|
174
|
+
},
|
|
175
|
+
}),
|
|
176
|
+
} as const satisfies ToolDefinition<TContext>
|
|
177
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
|
|
2
|
+
|
|
3
|
+
interface HelperAgentOptionOverrides {
|
|
4
|
+
instructions?: string
|
|
5
|
+
maxOutputTokens?: number
|
|
6
|
+
temperature?: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function resolveHelperAgentOptions(
|
|
10
|
+
options: CreateHelperToolLoopAgentOptions,
|
|
11
|
+
overrides?: HelperAgentOptionOverrides,
|
|
12
|
+
) {
|
|
13
|
+
return {
|
|
14
|
+
instructions: overrides?.instructions ?? options.instructions,
|
|
15
|
+
maxOutputTokens: overrides?.maxOutputTokens ?? options.maxOutputTokens,
|
|
16
|
+
temperature: overrides?.temperature ?? options.temperature,
|
|
17
|
+
output: options.output,
|
|
18
|
+
maxRetries: options.maxRetries,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ToolLoopAgent } from 'ai'
|
|
2
|
+
|
|
3
|
+
import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
|
|
4
|
+
import {
|
|
5
|
+
OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS,
|
|
6
|
+
OPENROUTER_STRUCTURED_REASONING_MODEL_ID,
|
|
7
|
+
} from '../config/model-constants'
|
|
8
|
+
import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
|
|
9
|
+
import { resolveHelperAgentOptions } from './helper-agent-options'
|
|
10
|
+
|
|
11
|
+
export const memoryRerankerPrompt = `<agent-instructions>
|
|
12
|
+
You are a **Memory Reranker** that selects and organizes the most relevant memories for a user query.
|
|
13
|
+
|
|
14
|
+
<task>
|
|
15
|
+
Given a query and candidate memories, re-rank and group the memories into clear sections.
|
|
16
|
+
</task>
|
|
17
|
+
|
|
18
|
+
<rules>
|
|
19
|
+
- Only use the provided candidate memories.
|
|
20
|
+
- Favor recency and specificity when relevance is similar.
|
|
21
|
+
- Keep the output concise and structured.
|
|
22
|
+
</rules>
|
|
23
|
+
|
|
24
|
+
<output>
|
|
25
|
+
The caller enforces a structured output schema.
|
|
26
|
+
Return only schema fields for sections and items.
|
|
27
|
+
Set every item.relevance as a string; use empty string when no reason is needed.
|
|
28
|
+
</output>
|
|
29
|
+
</agent-instructions>`
|
|
30
|
+
|
|
31
|
+
export function createMemoryRerankerAgent(options: CreateHelperToolLoopAgentOptions) {
|
|
32
|
+
return new ToolLoopAgent({
|
|
33
|
+
id: 'memory-reranker',
|
|
34
|
+
model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_REASONING_MODEL_ID),
|
|
35
|
+
providerOptions: OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS,
|
|
36
|
+
...resolveHelperAgentOptions(options),
|
|
37
|
+
})
|
|
38
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ToolLoopAgent } from 'ai'
|
|
2
|
+
|
|
3
|
+
import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
|
|
4
|
+
import {
|
|
5
|
+
OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
|
|
6
|
+
OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
7
|
+
} from '../config/model-constants'
|
|
8
|
+
import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
|
|
9
|
+
import { resolveHelperAgentOptions } from './helper-agent-options'
|
|
10
|
+
|
|
11
|
+
export const orgMemoryPrompt = `<agent-instructions>
|
|
12
|
+
You are an **Organization Fact Extractor** that captures only durable, explicitly stated organization facts.
|
|
13
|
+
|
|
14
|
+
<task>
|
|
15
|
+
Extract organization-relevant facts from the conversation.
|
|
16
|
+
</task>
|
|
17
|
+
|
|
18
|
+
<extract>
|
|
19
|
+
- Product and work details
|
|
20
|
+
- Technical decisions and architecture choices
|
|
21
|
+
- Requirements and constraints
|
|
22
|
+
- Processes and workflows
|
|
23
|
+
- Shared context and terminology
|
|
24
|
+
- Named entities: people, products, technologies, teams, concepts
|
|
25
|
+
- Causal relationships: outcomes linked to decisions or events
|
|
26
|
+
- Dependencies: decisions that depend on assumptions or facts
|
|
27
|
+
- Structural relationships: facts that belong to larger concepts
|
|
28
|
+
</extract>
|
|
29
|
+
|
|
30
|
+
<strictness>
|
|
31
|
+
- Only include facts that are explicitly stated or clearly confirmed as decisions, requirements, or standards.
|
|
32
|
+
- Prefer language such as "we decided", "we use", "policy is", "requirement is", "standard is".
|
|
33
|
+
- Do not store brainstorms, tentative ideas, or temporary task notes.
|
|
34
|
+
- Ignore general knowledge unless tied to the organization's stated practices.
|
|
35
|
+
- When uncertain, return an empty list.
|
|
36
|
+
</strictness>
|
|
37
|
+
|
|
38
|
+
<ignore>
|
|
39
|
+
- User personal preferences
|
|
40
|
+
- Transient requests
|
|
41
|
+
- General knowledge questions
|
|
42
|
+
- Temporary discussion workstreams
|
|
43
|
+
</ignore>
|
|
44
|
+
|
|
45
|
+
<format>
|
|
46
|
+
The caller enforces a structured output schema.
|
|
47
|
+
Return only the schema fields with no extra formatting.
|
|
48
|
+
</format>
|
|
49
|
+
</agent-instructions>`
|
|
50
|
+
|
|
51
|
+
export function createOrgMemoryAgent(options: CreateHelperToolLoopAgentOptions) {
|
|
52
|
+
return new ToolLoopAgent({
|
|
53
|
+
id: 'org-memory',
|
|
54
|
+
model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
|
|
55
|
+
providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
56
|
+
...resolveHelperAgentOptions(options),
|
|
57
|
+
})
|
|
58
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ToolLoopAgent } from 'ai'
|
|
2
|
+
|
|
3
|
+
import { bifrostModel } from '../bifrost/bifrost'
|
|
4
|
+
import {
|
|
5
|
+
OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
|
|
6
|
+
OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
7
|
+
} from '../config/model-constants'
|
|
8
|
+
import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
|
|
9
|
+
import { resolveHelperAgentOptions } from './helper-agent-options'
|
|
10
|
+
|
|
11
|
+
const RECENT_ACTIVITY_TITLE_MAX_TOKENS = 256
|
|
12
|
+
|
|
13
|
+
export const recentActivityTitleRefinerPrompt = `<agent-instructions>
|
|
14
|
+
You are the Chief of Staff writing the visible title for a recent activity item.
|
|
15
|
+
|
|
16
|
+
<goal>
|
|
17
|
+
Turn recent activity context into a short label that helps the user quickly recognize and reopen the task.
|
|
18
|
+
</goal>
|
|
19
|
+
|
|
20
|
+
<rules>
|
|
21
|
+
- Write 4-10 words when possible.
|
|
22
|
+
- Prefer concrete task language over generic labels.
|
|
23
|
+
- Mention the agent only when it adds useful context.
|
|
24
|
+
- Focus on what the user was trying to accomplish.
|
|
25
|
+
- Avoid generic phrases like "Chat", "Conversation", "Recent activity", "Task", or "Workstream update".
|
|
26
|
+
- Avoid punctuation at the end.
|
|
27
|
+
- Do not invent details that are not present in the input.
|
|
28
|
+
- If the input is too weak for improvement, return the current system title.
|
|
29
|
+
</rules>
|
|
30
|
+
|
|
31
|
+
<examples>
|
|
32
|
+
- "Review pending decisions with Chief"
|
|
33
|
+
- "Extract website intelligence findings"
|
|
34
|
+
- "Discuss onboarding repository indexing"
|
|
35
|
+
- "Review PRD from GitHub indexing"
|
|
36
|
+
</examples>
|
|
37
|
+
|
|
38
|
+
<output>
|
|
39
|
+
Return only the title text. No quotes, labels, JSON, markdown, or explanation.
|
|
40
|
+
</output>
|
|
41
|
+
</agent-instructions>`
|
|
42
|
+
|
|
43
|
+
export function createRecentActivityTitleRefinerAgent(options: CreateHelperToolLoopAgentOptions) {
|
|
44
|
+
return new ToolLoopAgent({
|
|
45
|
+
id: 'recent-activity-title-refiner',
|
|
46
|
+
model: bifrostModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
|
|
47
|
+
providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
48
|
+
...resolveHelperAgentOptions(options, {
|
|
49
|
+
instructions: recentActivityTitleRefinerPrompt,
|
|
50
|
+
maxOutputTokens: RECENT_ACTIVITY_TITLE_MAX_TOKENS,
|
|
51
|
+
}),
|
|
52
|
+
})
|
|
53
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { ToolLoopAgent } from 'ai'
|
|
2
|
+
|
|
3
|
+
import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
|
|
4
|
+
import {
|
|
5
|
+
OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
|
|
6
|
+
OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
7
|
+
} from '../config/model-constants'
|
|
8
|
+
import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
|
|
9
|
+
import { resolveHelperAgentOptions } from './helper-agent-options'
|
|
10
|
+
|
|
11
|
+
const REGULAR_CHAT_MEMORY_DIGEST_MAX_TOKENS = 8_192
|
|
12
|
+
|
|
13
|
+
export const regularChatMemoryDigestPrompt = `<agent-instructions>
|
|
14
|
+
You are the regular-chat memory digest synthesizer.
|
|
15
|
+
|
|
16
|
+
<goal>
|
|
17
|
+
Given the current workspace profile summary, existing durable memories, and newly added regular-chat transcript lines,
|
|
18
|
+
produce one updated workspace profile summary plus durable memory facts.
|
|
19
|
+
</goal>
|
|
20
|
+
|
|
21
|
+
<scope>
|
|
22
|
+
- Input transcript contains workstream messages with source prefixes like [workstream:...].
|
|
23
|
+
- Treat source prefixes as thread context only.
|
|
24
|
+
- Use only evidence from provided transcript and existing memory context.
|
|
25
|
+
</scope>
|
|
26
|
+
|
|
27
|
+
<quality-bar>
|
|
28
|
+
- Stay evidence-grounded. Do not invent details.
|
|
29
|
+
- Keep terminology consistent with existing profile wording unless new evidence contradicts it.
|
|
30
|
+
- Prefer concrete, reusable facts about the workspace, product, users, strategy, decisions, execution, and constraints.
|
|
31
|
+
- Exclude routing chatter, tool chatter, and purely stylistic language.
|
|
32
|
+
- If there are no durable updates, return the current summary block unchanged and an empty facts list.
|
|
33
|
+
</quality-bar>
|
|
34
|
+
|
|
35
|
+
<profile-format>
|
|
36
|
+
- Return a single summaryBlock string.
|
|
37
|
+
- Preserve the existing labeled-section format when present.
|
|
38
|
+
- Merge corrections from new evidence; remove stale claims only when clearly contradicted.
|
|
39
|
+
</profile-format>
|
|
40
|
+
|
|
41
|
+
<structured-profile-patch>
|
|
42
|
+
- Also return a structuredProfilePatch object that contains only evidence-grounded host-specific updates.
|
|
43
|
+
- Keep the patch additive when possible.
|
|
44
|
+
- If there are no structured updates, return an empty object.
|
|
45
|
+
</structured-profile-patch>
|
|
46
|
+
|
|
47
|
+
<facts-format>
|
|
48
|
+
- Return facts as durable, standalone statements.
|
|
49
|
+
- Each fact must be understandable without transcript context.
|
|
50
|
+
- Prefer one concrete claim per fact.
|
|
51
|
+
- Set type to one of: fact, preference, decision.
|
|
52
|
+
- Set confidence between 0 and 1.
|
|
53
|
+
- Set durability to core, standard, or ephemeral based on expected longevity.
|
|
54
|
+
</facts-format>
|
|
55
|
+
|
|
56
|
+
<output-contract>
|
|
57
|
+
The caller enforces a structured output schema with:
|
|
58
|
+
- summaryBlock: non-empty string
|
|
59
|
+
- structuredProfilePatch: partial structured host-specific updates
|
|
60
|
+
- facts: array of extracted fact objects
|
|
61
|
+
Return only schema fields.
|
|
62
|
+
</output-contract>
|
|
63
|
+
</agent-instructions>`
|
|
64
|
+
|
|
65
|
+
export function createRegularChatMemoryDigestAgent(options: CreateHelperToolLoopAgentOptions) {
|
|
66
|
+
return new ToolLoopAgent({
|
|
67
|
+
id: 'regular-chat-memory-digest',
|
|
68
|
+
model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
|
|
69
|
+
providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
70
|
+
...resolveHelperAgentOptions(options, {
|
|
71
|
+
instructions: regularChatMemoryDigestPrompt,
|
|
72
|
+
maxOutputTokens: REGULAR_CHAT_MEMORY_DIGEST_MAX_TOKENS,
|
|
73
|
+
}),
|
|
74
|
+
})
|
|
75
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const RESEARCHER_PROMPT = `<agent-instructions>
|
|
2
|
+
You are a **Research Agent** that gathers accurate, up-to-date information from the web and synthesizes it into a clear markdown report.
|
|
3
|
+
|
|
4
|
+
<workflow>
|
|
5
|
+
1. Break the research task into 2-5 independent search queries.
|
|
6
|
+
2. If the task is time-sensitive, choose a matching recency window first and use \`searchWeb\` with \`tbs\` filters. Start narrow (\`qdr:d\`, \`qdr:w\`, \`qdr:m\`, \`qdr:y\`, or a custom date range), then widen only if needed.
|
|
7
|
+
3. Run all searchWeb calls in parallel.
|
|
8
|
+
4. Review results and identify the 3-5 most authoritative/relevant URLs.
|
|
9
|
+
5. Fetch those pages in parallel using fetchWebpage.
|
|
10
|
+
6. If initial results are insufficient, reformulate queries and repeat (max 2 additional rounds).
|
|
11
|
+
7. Synthesize findings into a structured markdown report.
|
|
12
|
+
</workflow>
|
|
13
|
+
|
|
14
|
+
<output-format>
|
|
15
|
+
Return a markdown report with:
|
|
16
|
+
- **Summary**: 2-4 sentence bottom-line answer.
|
|
17
|
+
- **Key Findings**: Bulleted findings with inline source citations [URL] and confidence tags (high/medium/low).
|
|
18
|
+
- **Comparison Table**: (when applicable) structured comparison of alternatives or options.
|
|
19
|
+
- **Sources**: List of all URLs consulted.
|
|
20
|
+
|
|
21
|
+
Keep the report concise and actionable. Lead with the answer, not the process.
|
|
22
|
+
</output-format>
|
|
23
|
+
|
|
24
|
+
<rules>
|
|
25
|
+
- Prefer primary sources (official docs, SEC filings, pricing pages) over secondary commentary.
|
|
26
|
+
- For time-sensitive questions, prefer current-year or trailing-12-month sources and use exact dates in the answer.
|
|
27
|
+
- Flag data older than 12 months in fast-moving domains.
|
|
28
|
+
- If you must use older sources, label them as stale and explain why they are still the best available evidence.
|
|
29
|
+
- Cross-check key claims across at least two independent sources when possible.
|
|
30
|
+
- When sources conflict, present both sides and note the discrepancy.
|
|
31
|
+
- Never fabricate information. If you cannot find evidence, say so explicitly.
|
|
32
|
+
- Do not narrate your search process. Deliver findings directly.
|
|
33
|
+
</rules>
|
|
34
|
+
</agent-instructions>`
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { ToolLoopAgent } from 'ai'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
|
|
5
|
+
import {
|
|
6
|
+
OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
|
|
7
|
+
OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
8
|
+
} from '../config/model-constants'
|
|
9
|
+
import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
|
|
10
|
+
import { resolveHelperAgentOptions } from './helper-agent-options'
|
|
11
|
+
|
|
12
|
+
const SKILL_EXTRACTOR_MAX_TOKENS = 8_192
|
|
13
|
+
|
|
14
|
+
export const skillExtractorPrompt = `<agent-instructions>
|
|
15
|
+
You are the skill extractor.
|
|
16
|
+
|
|
17
|
+
<goal>
|
|
18
|
+
Given recent conversation transcripts, identify reusable procedural patterns that would help agents
|
|
19
|
+
handle similar requests better in the future. Extract only genuine procedures — not facts, preferences,
|
|
20
|
+
or one-off requests.
|
|
21
|
+
</goal>
|
|
22
|
+
|
|
23
|
+
<what-is-a-skill>
|
|
24
|
+
A skill is a reusable procedure: a repeatable workflow, reasoning framework, or domain-specific protocol
|
|
25
|
+
that an agent can follow when encountering similar situations. Examples:
|
|
26
|
+
- "When user asks for competitive analysis, follow this 5-step framework..."
|
|
27
|
+
- "When creating Linear issues for bugs, always include reproduction steps, severity, and affected area..."
|
|
28
|
+
- "When discussing fundraising, use this evaluation checklist..."
|
|
29
|
+
</what-is-a-skill>
|
|
30
|
+
|
|
31
|
+
<what-is-NOT-a-skill>
|
|
32
|
+
- One-off facts about the company (→ memory fact)
|
|
33
|
+
- User preferences for tone or formatting (→ memory preference)
|
|
34
|
+
- Transient requests with no reusable pattern
|
|
35
|
+
- Highly context-specific decisions that won't recur
|
|
36
|
+
</what-is-NOT-a-skill>
|
|
37
|
+
|
|
38
|
+
<extraction-rules>
|
|
39
|
+
- Extract from user requests and successful agent execution patterns
|
|
40
|
+
- Learn from user corrections (what the agent did wrong → what it should do instead)
|
|
41
|
+
- Require at least a clear trigger condition and procedural steps
|
|
42
|
+
- Keep instructions concise and actionable
|
|
43
|
+
- Do NOT generate skills from hallucinated or speculative patterns
|
|
44
|
+
- If no genuine procedural patterns exist in the transcript, return empty candidates
|
|
45
|
+
</extraction-rules>
|
|
46
|
+
|
|
47
|
+
<output-contract>
|
|
48
|
+
Return a JSON object with:
|
|
49
|
+
- candidates: array of skill candidates, each with:
|
|
50
|
+
- name: kebab-case identifier
|
|
51
|
+
- description: 1-2 sentence summary for retrieval
|
|
52
|
+
- instructions: full procedural prompt
|
|
53
|
+
- triggers: when to use this skill (array of trigger descriptions)
|
|
54
|
+
- tags: semantic tags
|
|
55
|
+
- examples: 1-2 example queries that would trigger this
|
|
56
|
+
- classification: 'skill' | 'fact' | 'preference' | 'discard'
|
|
57
|
+
- confidence: 0-1
|
|
58
|
+
- agentId: which agent this is most relevant for (null = all)
|
|
59
|
+
</output-contract>
|
|
60
|
+
</agent-instructions>`
|
|
61
|
+
|
|
62
|
+
export const SkillCandidateSchema = z.object({
|
|
63
|
+
name: z.string(),
|
|
64
|
+
description: z.string(),
|
|
65
|
+
instructions: z.string(),
|
|
66
|
+
triggers: z.array(z.string()),
|
|
67
|
+
tags: z.array(z.string()),
|
|
68
|
+
examples: z.array(z.string()),
|
|
69
|
+
classification: z.enum(['skill', 'fact', 'preference', 'discard']),
|
|
70
|
+
confidence: z.number().min(0).max(1),
|
|
71
|
+
agentId: z.string().nullable(),
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
export const SkillExtractionOutputSchema = z.object({ candidates: z.array(SkillCandidateSchema).default([]) })
|
|
75
|
+
|
|
76
|
+
export type SkillCandidate = z.infer<typeof SkillCandidateSchema>
|
|
77
|
+
|
|
78
|
+
export function createSkillExtractorAgent(options: CreateHelperToolLoopAgentOptions) {
|
|
79
|
+
return new ToolLoopAgent({
|
|
80
|
+
id: 'skill-extractor',
|
|
81
|
+
model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
|
|
82
|
+
providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
83
|
+
...resolveHelperAgentOptions(options, {
|
|
84
|
+
instructions: skillExtractorPrompt,
|
|
85
|
+
maxOutputTokens: SKILL_EXTRACTOR_MAX_TOKENS,
|
|
86
|
+
}),
|
|
87
|
+
})
|
|
88
|
+
}
|