@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,187 @@
1
+ import { agentDisplayNames, resolveAgentNameAlias } from '../config/agent-defaults'
2
+ import type { ChatMessageLike, ReadableUploadMetadataLike } from './chat-types'
3
+
4
+ export interface WorkstreamHistoryMessage {
5
+ role: 'user' | 'agent'
6
+ content: string
7
+ agentName?: string
8
+ }
9
+
10
+ function getAgentName(message: ChatMessageLike): string | undefined {
11
+ const metadata = message.metadata
12
+ if (!metadata || typeof metadata !== 'object') return undefined
13
+ const value = (metadata as Record<string, unknown>).agentName
14
+ if (typeof value !== 'string' || !value.trim()) return undefined
15
+ const resolvedAgentName = resolveAgentNameAlias(value)
16
+ return resolvedAgentName ? (agentDisplayNames[resolvedAgentName] ?? value.trim()) : value.trim()
17
+ }
18
+
19
+ function truncateTrackerText(value: string, maxChars: number): string {
20
+ return value.length <= maxChars ? value : `${value.slice(0, maxChars).trimEnd()}...`
21
+ }
22
+
23
+ function toTrackerToolLine(part: ChatMessageLike['parts'][number]): string | null {
24
+ if (typeof part !== 'object') return null
25
+ const record = part as Record<string, unknown>
26
+ const type = typeof record.type === 'string' ? record.type : null
27
+ if (!type?.startsWith('tool-')) return null
28
+
29
+ const toolName = type.slice('tool-'.length) || 'unknown'
30
+ const state = typeof record.state === 'string' ? record.state : null
31
+ const input = record.input
32
+ const output = record.output
33
+
34
+ const inputTask =
35
+ input && typeof input === 'object' && typeof (input as Record<string, unknown>).task === 'string'
36
+ ? truncateTrackerText(((input as Record<string, unknown>).task as string).trim(), 220)
37
+ : null
38
+ const outputSummary =
39
+ output && typeof output === 'object' && typeof (output as Record<string, unknown>).summary === 'string'
40
+ ? truncateTrackerText(((output as Record<string, unknown>).summary as string).trim(), 220)
41
+ : null
42
+ const outputResult =
43
+ output && typeof output === 'object' && typeof (output as Record<string, unknown>).result === 'string'
44
+ ? truncateTrackerText(((output as Record<string, unknown>).result as string).trim(), 220)
45
+ : null
46
+ const errorText = typeof record.errorText === 'string' ? truncateTrackerText(record.errorText.trim(), 220) : null
47
+
48
+ if (state === 'output-error') {
49
+ return errorText ? `Tool ${toolName} failed: ${errorText}` : `Tool ${toolName} failed.`
50
+ }
51
+
52
+ if (state !== 'output-available') {
53
+ return inputTask ? `Tool ${toolName} ran for: ${inputTask}` : `Tool ${toolName} ran.`
54
+ }
55
+
56
+ if (outputSummary) return `Tool ${toolName} completed: ${outputSummary}`
57
+ if (inputTask) return `Tool ${toolName} completed for: ${inputTask}`
58
+ if (outputResult) return `Tool ${toolName} completed: ${outputResult}`
59
+ return `Tool ${toolName} completed.`
60
+ }
61
+
62
+ export function extractMessageText(message: ChatMessageLike): string {
63
+ return message.parts
64
+ .flatMap((part) => (part.type === 'text' && typeof part.text === 'string' ? [part.text] : []))
65
+ .join('\n')
66
+ .trim()
67
+ }
68
+
69
+ export function extractTrackerMessageText(message: ChatMessageLike): string {
70
+ const textParts = message.parts.flatMap((part) =>
71
+ part.type === 'text' && typeof part.text === 'string' ? [part.text] : [],
72
+ )
73
+ const toolParts = message.parts
74
+ .map((part) => toTrackerToolLine(part))
75
+ .filter((value): value is string => Boolean(value))
76
+
77
+ return [...textParts, ...toolParts].join('\n').trim()
78
+ }
79
+
80
+ export function toHistoryMessages(messages: ChatMessageLike[], maxItems = 24): WorkstreamHistoryMessage[] {
81
+ return messages
82
+ .map((message): WorkstreamHistoryMessage | null => {
83
+ const content = extractMessageText(message)
84
+ if (!content) return null
85
+
86
+ if (message.role === 'user') {
87
+ return { role: 'user', content }
88
+ }
89
+ if (message.role === 'assistant') {
90
+ const agentName = getAgentName(message)
91
+ return { role: 'agent', content, ...(agentName ? { agentName } : {}) }
92
+ }
93
+ return null
94
+ })
95
+ .filter((message): message is WorkstreamHistoryMessage => message !== null)
96
+ .slice(-maxItems)
97
+ }
98
+
99
+ export function buildConversationSummary(params: {
100
+ userMessageText: string
101
+ assistantMessages: ChatMessageLike[]
102
+ }): string {
103
+ const lines: string[] = []
104
+ if (params.userMessageText.trim()) {
105
+ lines.push(`User: ${params.userMessageText.trim()}`)
106
+ }
107
+
108
+ for (const message of params.assistantMessages) {
109
+ const content = extractMessageText(message)
110
+ if (!content) continue
111
+ const agentName = getAgentName(message)
112
+ lines.push(agentName ? `${agentName}: ${content}` : `Assistant: ${content}`)
113
+ }
114
+
115
+ return lines.join('\n\n').trim()
116
+ }
117
+
118
+ export function buildAgentHistoryMessages(messages: ChatMessageLike[]): Array<{ content: string; agentName?: string }> {
119
+ return toHistoryMessages(messages)
120
+ .filter((message) => message.role === 'agent')
121
+ .map((message) => ({ content: message.content, ...(message.agentName ? { agentName: message.agentName } : {}) }))
122
+ }
123
+
124
+ export function appendCompactionContextToHistoryMessages(
125
+ historyMessages: WorkstreamHistoryMessage[],
126
+ params: { chatSummary?: string | null; persistedState?: unknown },
127
+ ): WorkstreamHistoryMessage[] {
128
+ const nextHistoryMessages = [...historyMessages]
129
+ const chatSummary = typeof params.chatSummary === 'string' ? params.chatSummary.trim() : ''
130
+ if (chatSummary) {
131
+ nextHistoryMessages.push({ role: 'agent', content: `Compacted chat summary:\n${chatSummary}` })
132
+ }
133
+
134
+ if (params.persistedState !== undefined && params.persistedState !== null) {
135
+ nextHistoryMessages.push({
136
+ role: 'agent',
137
+ content: `Structured workstream state:\n${JSON.stringify(params.persistedState)}`,
138
+ })
139
+ }
140
+
141
+ return nextHistoryMessages
142
+ }
143
+
144
+ export function buildReadableUploadMetadataContext(uploads: ReadableUploadMetadataLike[]): string | undefined {
145
+ if (uploads.length === 0) return undefined
146
+
147
+ return [
148
+ 'Uploaded files metadata:',
149
+ ...uploads.map((upload) => {
150
+ const sizeSegment = upload.sizeBytes === null ? '' : `, sizeBytes=${upload.sizeBytes}`
151
+ return `- ${upload.filename} (${upload.mediaType}${sizeSegment}) storageKey=${upload.storageKey}`
152
+ }),
153
+ ].join('\n')
154
+ }
155
+
156
+ export function toOptionalTrimmedString(value: unknown): string | null {
157
+ if (typeof value !== 'string') return null
158
+ const normalized = value.trim()
159
+ return normalized.length > 0 ? normalized : null
160
+ }
161
+
162
+ export function collectToolOutputErrors(params: {
163
+ responseMessage: ChatMessageLike
164
+ }): Array<{ toolName: string; toolCallId: string; errorText: string }> {
165
+ const errors: Array<{ toolName: string; toolCallId: string; errorText: string }> = []
166
+
167
+ for (const part of params.responseMessage.parts) {
168
+ if (typeof part !== 'object') continue
169
+ if (part.type !== undefined && typeof part.type !== 'string') continue
170
+ if (!part.type?.startsWith('tool-')) continue
171
+ if ((part as Record<string, unknown>).state !== 'output-error') continue
172
+
173
+ const toolName = part.type.slice('tool-'.length) || 'unknown'
174
+ const toolCallId =
175
+ typeof (part as Record<string, unknown>).toolCallId === 'string' && (part as Record<string, unknown>).toolCallId
176
+ ? ((part as Record<string, unknown>).toolCallId as string)
177
+ : 'unknown'
178
+ const errorTextRaw = (part as Record<string, unknown>).errorText
179
+ const errorText =
180
+ typeof errorTextRaw === 'string' && errorTextRaw.trim()
181
+ ? errorTextRaw.trim()
182
+ : 'Tool execution failed without explicit error text.'
183
+ errors.push({ toolName, toolCallId, errorText })
184
+ }
185
+
186
+ return errors
187
+ }
@@ -0,0 +1,301 @@
1
+ import { extractAgentMentions } from '../config/agent-defaults'
2
+
3
+ export type MessageRoute =
4
+ | { type: 'direct'; agents: [string] }
5
+ | { type: 'mentions'; agents: string[] }
6
+ | { type: 'group-default'; agents: [string] }
7
+
8
+ export type ReasoningProfileName = 'fast' | 'standard' | 'deep'
9
+
10
+ export interface ReasoningProfile {
11
+ name: ReasoningProfileName
12
+ maxSteps: number
13
+ toolCallBudget: number
14
+ maxInputTokensHint: number
15
+ }
16
+
17
+ export type HighImpactResponseClass =
18
+ | 'architecture-recommendation'
19
+ | 'gtm-strategy'
20
+ | 'pricing-positioning'
21
+ | 'product-capability-claim'
22
+ | 'community-targeting'
23
+ | 'mutating-operation'
24
+
25
+ export type PolicyClass = 'external-mutation' | 'security-privacy' | 'legal-compliance' | 'financial-decision'
26
+
27
+ export function uniqueMentionOrder(message: string): string[] {
28
+ const ordered: string[] = []
29
+ const seen = new Set<string>()
30
+ for (const mention of extractAgentMentions(message)) {
31
+ const agent = mention.agent
32
+ if (seen.has(agent)) continue
33
+ seen.add(agent)
34
+ ordered.push(agent)
35
+ }
36
+ return ordered
37
+ }
38
+
39
+ const GTM_STRONG_INTENT_PATTERNS: RegExp[] = [
40
+ /\bgo[-\s]?to[-\s]?market\b/i,
41
+ /\bgtm\b/i,
42
+ /\bcontent\s+marketing\b/i,
43
+ /\bcontent\s+strategy\b/i,
44
+ /\bdemand\s+generation\b/i,
45
+ /\bdistribution\s+strategy\b/i,
46
+ /\bpositioning\s+strategy\b/i,
47
+ /\blaunch\s+strategy\b/i,
48
+ /\blaunch\s+plan\b/i,
49
+ ]
50
+
51
+ const GTM_WEAK_INTENT_PATTERNS: RegExp[] = [/\blaunch\b/i, /\bcommunity\b/i, /\bdistribution\b/i, /\bpositioning\b/i]
52
+
53
+ export function isGtmIntentMessage(message: string): boolean {
54
+ const text = message.trim()
55
+ if (!text) return false
56
+
57
+ if (GTM_STRONG_INTENT_PATTERNS.some((pattern) => pattern.test(text))) {
58
+ return true
59
+ }
60
+
61
+ let weakMatches = 0
62
+ for (const pattern of GTM_WEAK_INTENT_PATTERNS) {
63
+ if (pattern.test(text)) weakMatches += 1
64
+ if (weakMatches >= 2) return true
65
+ }
66
+ return false
67
+ }
68
+
69
+ const HIGH_IMPACT_CLASS_PATTERNS: Array<{ className: HighImpactResponseClass; patterns: RegExp[] }> = [
70
+ {
71
+ className: 'architecture-recommendation',
72
+ patterns: [
73
+ /\barchitecture\b/i,
74
+ /\btech(?:nical)?\s+stack\b/i,
75
+ /\binfra(?:structure)?\b/i,
76
+ /\brefactor\b/i,
77
+ /\bsystem\s+design\b/i,
78
+ ],
79
+ },
80
+ {
81
+ className: 'gtm-strategy',
82
+ patterns: [
83
+ /\bgo[-\s]?to[-\s]?market\b/i,
84
+ /\bgtm\b/i,
85
+ /\bdistribution\s+strategy\b/i,
86
+ /\blaunch\s+strategy\b/i,
87
+ /\bcontent\s+strategy\b/i,
88
+ /\bdemand\s+gen(?:eration)?\b/i,
89
+ ],
90
+ },
91
+ {
92
+ className: 'pricing-positioning',
93
+ patterns: [/\bpricing\b/i, /\bpositioning\b/i, /\bpackaging\b/i, /\bfreemium\b/i, /\bbilling\b/i],
94
+ },
95
+ {
96
+ className: 'product-capability-claim',
97
+ patterns: [
98
+ /\bdoes\s+it\s+(already\s+)?(support|have|do)\b/i,
99
+ /\bwhat\s+can\s+(it|we)\s+do\b/i,
100
+ /\bis\s+this\s+implemented\b/i,
101
+ /\bcurrent(?:ly)?\s+supports?\b/i,
102
+ /\bcapabilities?\b/i,
103
+ ],
104
+ },
105
+ {
106
+ className: 'community-targeting',
107
+ patterns: [
108
+ /\bwhere\s+to\s+post\b/i,
109
+ /\bcommunity\b/i,
110
+ /\btarget\s+audience\b/i,
111
+ /\bchannels?\b/i,
112
+ /\bdistribution\s+channels?\b/i,
113
+ ],
114
+ },
115
+ {
116
+ className: 'mutating-operation',
117
+ patterns: [
118
+ /\bdelete\s+(issue|comment|task|record|workspace|project)\b/i,
119
+ /\bremove\s+(issue|comment|task|record|workspace|project)\b/i,
120
+ /\bupdate\s+(issue|comment|task|record|workspace|project)\b/i,
121
+ /\bcreate\s+(issue|comment|task|record|workspace|project)\b/i,
122
+ /\bchange\s+(issue|comment|task|record|workspace|project)\b/i,
123
+ /\barchive\b/i,
124
+ /\bskip\s+onboarding\b/i,
125
+ /\bproceed\s+in\s+onboarding\b/i,
126
+ ],
127
+ },
128
+ ]
129
+
130
+ const POLICY_CLASS_PATTERNS: Array<{ className: PolicyClass; patterns: RegExp[] }> = [
131
+ {
132
+ className: 'external-mutation',
133
+ patterns: [
134
+ /\bdelete\s+(issue|comment|task|record|workspace|project)\b/i,
135
+ /\bremove\s+(issue|comment|task|record|workspace|project)\b/i,
136
+ /\bupdate\s+(issue|comment|task|record|workspace|project)\b/i,
137
+ /\bcreate\s+(issue|comment|task|record|workspace|project)\b/i,
138
+ /\bchange\s+(issue|comment|task|record|workspace|project)\b/i,
139
+ /\barchive\b/i,
140
+ /\bskip\s+onboarding\b/i,
141
+ /\bproceed\s+in\s+onboarding\b/i,
142
+ ],
143
+ },
144
+ {
145
+ className: 'security-privacy',
146
+ patterns: [
147
+ /\bsecurity\b/i,
148
+ /\bprivacy\b/i,
149
+ /\bpii\b/i,
150
+ /\bauth(?:entication|orization)?\b/i,
151
+ /\bencrypt(?:ion|ed)?\b/i,
152
+ /\bpermission(?:s)?\b/i,
153
+ /\baccess\s+control\b/i,
154
+ /\bsecret(?:s)?\b/i,
155
+ ],
156
+ },
157
+ {
158
+ className: 'legal-compliance',
159
+ patterns: [
160
+ /\blegal\b/i,
161
+ /\bcompliance\b/i,
162
+ /\bregulation(?:s)?\b/i,
163
+ /\bgdpr\b/i,
164
+ /\bhipaa\b/i,
165
+ /\bsoc\s*2\b/i,
166
+ /\blicense\b/i,
167
+ /\bterms?\b/i,
168
+ ],
169
+ },
170
+ {
171
+ className: 'financial-decision',
172
+ patterns: [
173
+ /\bpricing\b/i,
174
+ /\bbilling\b/i,
175
+ /\brevenue\b/i,
176
+ /\bbudget\b/i,
177
+ /\bburn\s+rate\b/i,
178
+ /\brunway\b/i,
179
+ /\bfundrais(?:e|ing)\b/i,
180
+ /\bcash\s+flow\b/i,
181
+ ],
182
+ },
183
+ ]
184
+
185
+ const REASONING_PROFILES: Record<ReasoningProfileName, ReasoningProfile> = {
186
+ fast: { name: 'fast', maxSteps: 5, toolCallBudget: 3, maxInputTokensHint: 12_000 },
187
+ standard: { name: 'standard', maxSteps: 10, toolCallBudget: 6, maxInputTokensHint: 24_000 },
188
+ deep: { name: 'deep', maxSteps: 15, toolCallBudget: 10, maxInputTokensHint: 36_000 },
189
+ }
190
+
191
+ const STRATEGIC_COMPLEXITY_PATTERNS: RegExp[] = [
192
+ /\bstrategy\b/i,
193
+ /\btrade[-\s]?off\b/i,
194
+ /\bcompare\b/i,
195
+ /\broadmap\b/i,
196
+ /\barchitecture\b/i,
197
+ /\bconstraints?\b/i,
198
+ /\bprioriti(?:y|es)\b/i,
199
+ ]
200
+
201
+ const CROSS_SOURCE_COMPLEXITY_PATTERNS: RegExp[] = [
202
+ /\bcitation\b/i,
203
+ /\bevidence\b/i,
204
+ /\bsource\b/i,
205
+ /\bground(?:ed|ing)\b/i,
206
+ /\brepo\b/i,
207
+ /\bmemory\b/i,
208
+ ]
209
+
210
+ const STANDARD_FLOOR_INTENT_PATTERNS: RegExp[] = [
211
+ /\bplan(?:ning)?\b/i,
212
+ /\bthink(?:ing)?\b/i,
213
+ /\bcareful(?:ly)?\b/i,
214
+ /\bimportant\b/i,
215
+ /\bcritical(?:ly)?\b/i,
216
+ /\bthorough(?:ly)?\b/i,
217
+ ]
218
+
219
+ export function classifyHighImpactResponse(params: { message: string }): { classes: HighImpactResponseClass[] } {
220
+ const message = params.message.trim()
221
+ if (!message) return { classes: [] }
222
+
223
+ const classes: HighImpactResponseClass[] = []
224
+ for (const entry of HIGH_IMPACT_CLASS_PATTERNS) {
225
+ if (entry.patterns.some((pattern) => pattern.test(message))) {
226
+ classes.push(entry.className)
227
+ }
228
+ }
229
+
230
+ return { classes }
231
+ }
232
+
233
+ export function classifyPolicyClasses(params: { message: string }): { classes: PolicyClass[] } {
234
+ const message = params.message.trim()
235
+ if (!message) return { classes: [] }
236
+
237
+ const classes: PolicyClass[] = []
238
+ for (const entry of POLICY_CLASS_PATTERNS) {
239
+ if (entry.patterns.some((pattern) => pattern.test(message))) {
240
+ classes.push(entry.className)
241
+ }
242
+ }
243
+
244
+ return { classes }
245
+ }
246
+
247
+ export function resolveReasoningProfile(params: {
248
+ message: string
249
+ forceDeep?: boolean
250
+ explicitProfile?: ReasoningProfileName
251
+ }): ReasoningProfile {
252
+ if (params.explicitProfile) {
253
+ return REASONING_PROFILES[params.explicitProfile]
254
+ }
255
+
256
+ const text = params.message.trim()
257
+ if (!text) {
258
+ return REASONING_PROFILES.standard
259
+ }
260
+
261
+ let score = 0
262
+ if (text.length >= 700) score += 3
263
+ else if (text.length >= 350) score += 2
264
+ else if (text.length >= 180) score += 1
265
+
266
+ const numberedListCount = (text.match(/\n\s*\d+\./g) ?? []).length
267
+ if (numberedListCount >= 3) score += 2
268
+ else if (numberedListCount >= 1) score += 1
269
+
270
+ if (STRATEGIC_COMPLEXITY_PATTERNS.some((pattern) => pattern.test(text))) score += 2
271
+ if (CROSS_SOURCE_COMPLEXITY_PATTERNS.some((pattern) => pattern.test(text))) score += 2
272
+ if (params.forceDeep) score += 2
273
+ if (STANDARD_FLOOR_INTENT_PATTERNS.some((pattern) => pattern.test(text))) {
274
+ score += 3
275
+ }
276
+
277
+ if (score >= 7) return REASONING_PROFILES.deep
278
+ if (score >= 3) return REASONING_PROFILES.standard
279
+ return REASONING_PROFILES.fast
280
+ }
281
+
282
+ export function resolveMessageRoute(params: {
283
+ workstreamMode: 'direct' | 'group'
284
+ workstreamAgentId?: string
285
+ message: string
286
+ }): MessageRoute {
287
+ if (params.workstreamMode === 'direct') {
288
+ return { type: 'direct', agents: [params.workstreamAgentId ?? 'chief'] }
289
+ }
290
+
291
+ const mentions = uniqueMentionOrder(params.message)
292
+ if (mentions.length > 0) {
293
+ return { type: 'mentions', agents: mentions }
294
+ }
295
+
296
+ if (isGtmIntentMessage(params.message)) {
297
+ return { type: 'group-default', agents: ['cmo'] }
298
+ }
299
+
300
+ return { type: 'group-default', agents: ['chief'] }
301
+ }