@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.
Files changed (153) hide show
  1. package/infrastructure/schema/00_workstream.surql +55 -0
  2. package/infrastructure/schema/01_memory.surql +47 -0
  3. package/infrastructure/schema/02_execution_plan.surql +62 -0
  4. package/infrastructure/schema/03_learned_skill.surql +32 -0
  5. package/infrastructure/schema/04_runtime_bootstrap.surql +8 -0
  6. package/package.json +128 -0
  7. package/src/ai/definitions.ts +308 -0
  8. package/src/bifrost/bifrost.ts +256 -0
  9. package/src/config/agent-defaults.ts +99 -0
  10. package/src/config/constants.ts +33 -0
  11. package/src/config/env-shapes.ts +122 -0
  12. package/src/config/logger.ts +29 -0
  13. package/src/config/model-constants.ts +31 -0
  14. package/src/config/search.ts +17 -0
  15. package/src/config/workstream-defaults.ts +68 -0
  16. package/src/db/base.service.ts +55 -0
  17. package/src/db/cursor-pagination.ts +73 -0
  18. package/src/db/memory-query-builder.ts +207 -0
  19. package/src/db/memory-store.helpers.ts +118 -0
  20. package/src/db/memory-store.rows.ts +29 -0
  21. package/src/db/memory-store.ts +974 -0
  22. package/src/db/memory-types.ts +193 -0
  23. package/src/db/memory.ts +505 -0
  24. package/src/db/record-id.ts +78 -0
  25. package/src/db/service.ts +932 -0
  26. package/src/db/startup.ts +152 -0
  27. package/src/db/tables.ts +20 -0
  28. package/src/document/org-document-chunking.ts +224 -0
  29. package/src/document/parsing.ts +40 -0
  30. package/src/embeddings/provider.ts +76 -0
  31. package/src/index.ts +302 -0
  32. package/src/queues/context-compaction.queue.ts +82 -0
  33. package/src/queues/document-processor.queue.ts +118 -0
  34. package/src/queues/memory-consolidation.queue.ts +65 -0
  35. package/src/queues/post-chat-memory.queue.ts +128 -0
  36. package/src/queues/recent-activity-title-refinement.queue.ts +69 -0
  37. package/src/queues/regular-chat-memory-digest.config.ts +12 -0
  38. package/src/queues/regular-chat-memory-digest.queue.ts +73 -0
  39. package/src/queues/skill-extraction.config.ts +9 -0
  40. package/src/queues/skill-extraction.queue.ts +62 -0
  41. package/src/redis/connection.ts +176 -0
  42. package/src/redis/index.ts +30 -0
  43. package/src/redis/org-memory-lock.ts +43 -0
  44. package/src/redis/redis-lease-lock.ts +158 -0
  45. package/src/runtime/agent-contract.ts +1 -0
  46. package/src/runtime/agent-prompt-context.ts +119 -0
  47. package/src/runtime/agent-runtime-policy.ts +192 -0
  48. package/src/runtime/agent-stream-helpers.ts +117 -0
  49. package/src/runtime/agent-types.ts +22 -0
  50. package/src/runtime/approval-continuation.ts +16 -0
  51. package/src/runtime/chat-attachments.ts +46 -0
  52. package/src/runtime/chat-message.ts +10 -0
  53. package/src/runtime/chat-request-routing.ts +21 -0
  54. package/src/runtime/chat-run-orchestration.ts +25 -0
  55. package/src/runtime/chat-run-registry.ts +20 -0
  56. package/src/runtime/chat-types.ts +18 -0
  57. package/src/runtime/context-compaction-constants.ts +11 -0
  58. package/src/runtime/context-compaction-runtime.ts +86 -0
  59. package/src/runtime/context-compaction.ts +909 -0
  60. package/src/runtime/execution-plan.ts +59 -0
  61. package/src/runtime/helper-model.ts +405 -0
  62. package/src/runtime/indexed-repositories-policy.ts +28 -0
  63. package/src/runtime/instruction-sections.ts +8 -0
  64. package/src/runtime/llm-content.ts +71 -0
  65. package/src/runtime/memory-block.ts +264 -0
  66. package/src/runtime/memory-digest-policy.ts +14 -0
  67. package/src/runtime/memory-format.ts +8 -0
  68. package/src/runtime/memory-pipeline.ts +570 -0
  69. package/src/runtime/memory-prompts-fact.ts +47 -0
  70. package/src/runtime/memory-prompts-parse.ts +3 -0
  71. package/src/runtime/memory-prompts-update.ts +37 -0
  72. package/src/runtime/memory-scope.ts +43 -0
  73. package/src/runtime/plugin-types.ts +10 -0
  74. package/src/runtime/retrieval-adapters.ts +25 -0
  75. package/src/runtime/retrieval-pipeline.ts +3 -0
  76. package/src/runtime/runtime-extensions.ts +154 -0
  77. package/src/runtime/skill-extraction-policy.ts +3 -0
  78. package/src/runtime/team-consultation-orchestrator.ts +245 -0
  79. package/src/runtime/team-consultation-prompts.ts +32 -0
  80. package/src/runtime/title-helpers.ts +12 -0
  81. package/src/runtime/turn-lifecycle.ts +28 -0
  82. package/src/runtime/workstream-chat-helpers.ts +187 -0
  83. package/src/runtime/workstream-routing-policy.ts +301 -0
  84. package/src/runtime/workstream-state.ts +261 -0
  85. package/src/services/attachment.service.ts +159 -0
  86. package/src/services/chat-attachments.service.ts +17 -0
  87. package/src/services/chat-run-registry.service.ts +3 -0
  88. package/src/services/context-compaction-runtime.ts +13 -0
  89. package/src/services/context-compaction.service.ts +115 -0
  90. package/src/services/document-chunk.service.ts +141 -0
  91. package/src/services/execution-plan.service.ts +890 -0
  92. package/src/services/learned-skill.service.ts +328 -0
  93. package/src/services/memory-assessment.service.ts +43 -0
  94. package/src/services/memory.service.ts +807 -0
  95. package/src/services/memory.utils.ts +84 -0
  96. package/src/services/mutating-approval.service.ts +110 -0
  97. package/src/services/recent-activity-title.service.ts +74 -0
  98. package/src/services/recent-activity.service.ts +397 -0
  99. package/src/services/workstream-change-tracker.service.ts +313 -0
  100. package/src/services/workstream-message.service.ts +283 -0
  101. package/src/services/workstream-title.service.ts +58 -0
  102. package/src/services/workstream-turn-preparation.ts +1340 -0
  103. package/src/services/workstream-turn.ts +37 -0
  104. package/src/services/workstream.service.ts +854 -0
  105. package/src/services/workstream.types.ts +118 -0
  106. package/src/storage/attachment-parser.ts +101 -0
  107. package/src/storage/attachment-storage.service.ts +391 -0
  108. package/src/storage/attachments.types.ts +11 -0
  109. package/src/storage/attachments.utils.ts +58 -0
  110. package/src/storage/generated-document-storage.service.ts +55 -0
  111. package/src/system-agents/agent-result.ts +27 -0
  112. package/src/system-agents/context-compacter.agent.ts +46 -0
  113. package/src/system-agents/delegated-agent-factory.ts +177 -0
  114. package/src/system-agents/helper-agent-options.ts +20 -0
  115. package/src/system-agents/memory-reranker.agent.ts +38 -0
  116. package/src/system-agents/memory.agent.ts +58 -0
  117. package/src/system-agents/recent-activity-title-refiner.agent.ts +53 -0
  118. package/src/system-agents/regular-chat-memory-digest.agent.ts +75 -0
  119. package/src/system-agents/researcher.agent.ts +34 -0
  120. package/src/system-agents/skill-extractor.agent.ts +88 -0
  121. package/src/system-agents/skill-manager.agent.ts +80 -0
  122. package/src/system-agents/title-generator.agent.ts +42 -0
  123. package/src/system-agents/workstream-tracker.agent.ts +58 -0
  124. package/src/tools/execution-plan.tool.ts +163 -0
  125. package/src/tools/fetch-webpage.tool.ts +132 -0
  126. package/src/tools/firecrawl-client.ts +12 -0
  127. package/src/tools/memory-block.tool.ts +55 -0
  128. package/src/tools/read-file-parts.tool.ts +80 -0
  129. package/src/tools/remember-memory.tool.ts +85 -0
  130. package/src/tools/research-topic.tool.ts +15 -0
  131. package/src/tools/search-tools.ts +55 -0
  132. package/src/tools/search-web.tool.ts +175 -0
  133. package/src/tools/team-think.tool.ts +125 -0
  134. package/src/tools/tool-contract.ts +21 -0
  135. package/src/tools/user-questions.tool.ts +18 -0
  136. package/src/utils/async.ts +50 -0
  137. package/src/utils/date-time.ts +34 -0
  138. package/src/utils/error.ts +10 -0
  139. package/src/utils/errors.ts +28 -0
  140. package/src/utils/hono-error-handler.ts +71 -0
  141. package/src/utils/string.ts +51 -0
  142. package/src/workers/bootstrap.ts +44 -0
  143. package/src/workers/memory-consolidation.worker.ts +318 -0
  144. package/src/workers/regular-chat-memory-digest.helpers.ts +100 -0
  145. package/src/workers/regular-chat-memory-digest.runner.ts +363 -0
  146. package/src/workers/regular-chat-memory-digest.worker.ts +22 -0
  147. package/src/workers/skill-extraction.runner.ts +331 -0
  148. package/src/workers/skill-extraction.worker.ts +22 -0
  149. package/src/workers/utils/repo-indexer-chunker.ts +331 -0
  150. package/src/workers/utils/repo-structure-extractor.ts +645 -0
  151. package/src/workers/utils/repomix-process-concurrency.ts +65 -0
  152. package/src/workers/utils/sandbox-error.ts +5 -0
  153. 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
+ }