@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,261 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
const DecisionConfidenceSchema = z.enum(['high', 'medium', 'low'])
|
|
4
|
+
const StateSourceSchema = z.enum(['user', 'agent'])
|
|
5
|
+
|
|
6
|
+
const WorkstreamPlanSchema = z.object({
|
|
7
|
+
id: z.string(),
|
|
8
|
+
text: z.string(),
|
|
9
|
+
source: StateSourceSchema,
|
|
10
|
+
approved: z.boolean(),
|
|
11
|
+
timestamp: z.number(),
|
|
12
|
+
sourceMessageIds: z.array(z.string()).default([]),
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
const WorkstreamConstraintSchema = z.object({
|
|
16
|
+
id: z.string(),
|
|
17
|
+
text: z.string(),
|
|
18
|
+
source: StateSourceSchema,
|
|
19
|
+
approved: z.boolean(),
|
|
20
|
+
timestamp: z.number(),
|
|
21
|
+
sourceMessageIds: z.array(z.string()).default([]),
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const WorkstreamDecisionSchema = z.object({
|
|
25
|
+
id: z.string(),
|
|
26
|
+
decision: z.string(),
|
|
27
|
+
rationale: z.string(),
|
|
28
|
+
agent: z.string(),
|
|
29
|
+
sourceMessageIds: z.array(z.string()).default([]),
|
|
30
|
+
confidence: DecisionConfidenceSchema,
|
|
31
|
+
timestamp: z.number(),
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const WorkstreamTaskSchema = z.object({
|
|
35
|
+
id: z.string(),
|
|
36
|
+
title: z.string(),
|
|
37
|
+
status: z.enum(['open', 'in-progress', 'done', 'blocked']),
|
|
38
|
+
owner: z.string(),
|
|
39
|
+
externalId: z.string().optional(),
|
|
40
|
+
source: StateSourceSchema,
|
|
41
|
+
timestamp: z.number(),
|
|
42
|
+
sourceMessageIds: z.array(z.string()).default([]),
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const WorkstreamQuestionSchema = z.object({
|
|
46
|
+
id: z.string(),
|
|
47
|
+
text: z.string(),
|
|
48
|
+
source: StateSourceSchema,
|
|
49
|
+
timestamp: z.number(),
|
|
50
|
+
sourceMessageIds: z.array(z.string()).default([]),
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const WorkstreamArtifactSchema = z.object({
|
|
54
|
+
id: z.string(),
|
|
55
|
+
name: z.string(),
|
|
56
|
+
type: z.string(),
|
|
57
|
+
pointer: z.string(),
|
|
58
|
+
timestamp: z.number(),
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const WorkstreamAgentNoteSchema = z.object({
|
|
62
|
+
id: z.string(),
|
|
63
|
+
agent: z.string(),
|
|
64
|
+
summary: z.string(),
|
|
65
|
+
timestamp: z.number(),
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
export const WorkstreamStateSchema = z.object({
|
|
69
|
+
currentPlan: WorkstreamPlanSchema.nullable(),
|
|
70
|
+
activeConstraints: z.array(WorkstreamConstraintSchema),
|
|
71
|
+
keyDecisions: z.array(WorkstreamDecisionSchema),
|
|
72
|
+
tasks: z.array(WorkstreamTaskSchema),
|
|
73
|
+
openQuestions: z.array(WorkstreamQuestionSchema),
|
|
74
|
+
risks: z.array(z.string()),
|
|
75
|
+
artifacts: z.array(WorkstreamArtifactSchema),
|
|
76
|
+
agentContributions: z.array(WorkstreamAgentNoteSchema),
|
|
77
|
+
approvedBy: z.string().optional(),
|
|
78
|
+
approvedAt: z.number().int().optional(),
|
|
79
|
+
approvalMessageId: z.string().optional(),
|
|
80
|
+
approvalNote: z.string().optional(),
|
|
81
|
+
lastUpdated: z.number(),
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
export type WorkstreamState = z.infer<typeof WorkstreamStateSchema>
|
|
85
|
+
|
|
86
|
+
export const WorkstreamStateDeltaSchema = z.object({
|
|
87
|
+
currentPlan: z.string().nullable().optional(),
|
|
88
|
+
newDecisions: z
|
|
89
|
+
.array(
|
|
90
|
+
z.object({
|
|
91
|
+
decision: z.string(),
|
|
92
|
+
rationale: z.string(),
|
|
93
|
+
agent: z.string(),
|
|
94
|
+
sourceMessageIds: z.array(z.string()),
|
|
95
|
+
confidence: DecisionConfidenceSchema,
|
|
96
|
+
}),
|
|
97
|
+
)
|
|
98
|
+
.optional(),
|
|
99
|
+
resolvedQuestions: z.array(z.string()).optional(),
|
|
100
|
+
newQuestions: z.array(z.string()).optional(),
|
|
101
|
+
newConstraints: z.array(z.string()).optional(),
|
|
102
|
+
newRisks: z.array(z.string()).optional(),
|
|
103
|
+
taskUpdates: z
|
|
104
|
+
.array(
|
|
105
|
+
z.object({
|
|
106
|
+
title: z.string(),
|
|
107
|
+
status: WorkstreamTaskSchema.shape.status,
|
|
108
|
+
owner: z.string(),
|
|
109
|
+
externalId: z.string().nullable(),
|
|
110
|
+
sourceMessageIds: z.array(z.string()),
|
|
111
|
+
}),
|
|
112
|
+
)
|
|
113
|
+
.optional(),
|
|
114
|
+
artifacts: z.array(z.object({ name: z.string(), type: z.string(), pointer: z.string() })).optional(),
|
|
115
|
+
agentNote: z.object({ agent: z.string(), summary: z.string() }).optional(),
|
|
116
|
+
conflicts: z
|
|
117
|
+
.array(z.object({ newFact: z.string(), conflictsWith: z.string(), recommendation: z.string() }))
|
|
118
|
+
.optional(),
|
|
119
|
+
approvedBy: z.string().optional(),
|
|
120
|
+
approvedAt: z.number().int().optional(),
|
|
121
|
+
approvalMessageId: z.string().optional(),
|
|
122
|
+
approvalNote: z.string().optional(),
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
export type WorkstreamStateDelta = z.infer<typeof WorkstreamStateDeltaSchema>
|
|
126
|
+
|
|
127
|
+
const StructuredWorkstreamPlanDeltaSchema = z.object({
|
|
128
|
+
action: z.enum(['unchanged', 'clear', 'set']),
|
|
129
|
+
text: z.string().nullable(),
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
const StructuredWorkstreamDecisionDeltaSchema = z.object({
|
|
133
|
+
decision: z.string(),
|
|
134
|
+
rationale: z.string(),
|
|
135
|
+
agent: z.string(),
|
|
136
|
+
sourceMessageIds: z.array(z.string()),
|
|
137
|
+
confidence: DecisionConfidenceSchema,
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
const StructuredWorkstreamTaskDeltaSchema = z.object({
|
|
141
|
+
title: z.string(),
|
|
142
|
+
status: WorkstreamTaskSchema.shape.status,
|
|
143
|
+
owner: z.string(),
|
|
144
|
+
externalId: z.string().nullable(),
|
|
145
|
+
sourceMessageIds: z.array(z.string()),
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
const StructuredWorkstreamArtifactDeltaSchema = z.object({ name: z.string(), type: z.string(), pointer: z.string() })
|
|
149
|
+
|
|
150
|
+
const StructuredWorkstreamAgentNoteDeltaSchema = z.object({ agent: z.string(), summary: z.string() })
|
|
151
|
+
|
|
152
|
+
const StructuredWorkstreamConflictDeltaSchema = z.object({
|
|
153
|
+
newFact: z.string(),
|
|
154
|
+
conflictsWith: z.string(),
|
|
155
|
+
recommendation: z.string(),
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
export const StructuredWorkstreamStateDeltaSchema = z.object({
|
|
159
|
+
currentPlan: StructuredWorkstreamPlanDeltaSchema,
|
|
160
|
+
newDecisions: z.array(StructuredWorkstreamDecisionDeltaSchema),
|
|
161
|
+
resolvedQuestions: z.array(z.string()),
|
|
162
|
+
newQuestions: z.array(z.string()),
|
|
163
|
+
newConstraints: z.array(z.string()),
|
|
164
|
+
newRisks: z.array(z.string()),
|
|
165
|
+
taskUpdates: z.array(StructuredWorkstreamTaskDeltaSchema),
|
|
166
|
+
artifacts: z.array(StructuredWorkstreamArtifactDeltaSchema),
|
|
167
|
+
agentNote: StructuredWorkstreamAgentNoteDeltaSchema.nullable(),
|
|
168
|
+
conflicts: z.array(StructuredWorkstreamConflictDeltaSchema),
|
|
169
|
+
approvedBy: z.string().nullable(),
|
|
170
|
+
approvedAt: z.number().int().nullable(),
|
|
171
|
+
approvalMessageId: z.string().nullable(),
|
|
172
|
+
approvalNote: z.string().nullable(),
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
export type StructuredWorkstreamStateDelta = z.infer<typeof StructuredWorkstreamStateDeltaSchema>
|
|
176
|
+
|
|
177
|
+
export function createEmptyStructuredWorkstreamStateDelta(): StructuredWorkstreamStateDelta {
|
|
178
|
+
return {
|
|
179
|
+
currentPlan: { action: 'unchanged', text: null },
|
|
180
|
+
newDecisions: [],
|
|
181
|
+
resolvedQuestions: [],
|
|
182
|
+
newQuestions: [],
|
|
183
|
+
newConstraints: [],
|
|
184
|
+
newRisks: [],
|
|
185
|
+
taskUpdates: [],
|
|
186
|
+
artifacts: [],
|
|
187
|
+
agentNote: null,
|
|
188
|
+
conflicts: [],
|
|
189
|
+
approvedBy: null,
|
|
190
|
+
approvedAt: null,
|
|
191
|
+
approvalMessageId: null,
|
|
192
|
+
approvalNote: null,
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function parseStructuredWorkstreamStateDelta(value: unknown): WorkstreamStateDelta {
|
|
197
|
+
const parsed = StructuredWorkstreamStateDeltaSchema.parse(value)
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
...(parsed.currentPlan.action === 'clear'
|
|
201
|
+
? { currentPlan: null }
|
|
202
|
+
: parsed.currentPlan.action === 'set' && typeof parsed.currentPlan.text === 'string'
|
|
203
|
+
? { currentPlan: parsed.currentPlan.text }
|
|
204
|
+
: {}),
|
|
205
|
+
...(parsed.newDecisions.length > 0 ? { newDecisions: parsed.newDecisions } : {}),
|
|
206
|
+
...(parsed.resolvedQuestions.length > 0 ? { resolvedQuestions: parsed.resolvedQuestions } : {}),
|
|
207
|
+
...(parsed.newQuestions.length > 0 ? { newQuestions: parsed.newQuestions } : {}),
|
|
208
|
+
...(parsed.newConstraints.length > 0 ? { newConstraints: parsed.newConstraints } : {}),
|
|
209
|
+
...(parsed.newRisks.length > 0 ? { newRisks: parsed.newRisks } : {}),
|
|
210
|
+
...(parsed.taskUpdates.length > 0
|
|
211
|
+
? {
|
|
212
|
+
taskUpdates: parsed.taskUpdates.map((taskUpdate) => ({
|
|
213
|
+
title: taskUpdate.title,
|
|
214
|
+
status: taskUpdate.status,
|
|
215
|
+
owner: taskUpdate.owner,
|
|
216
|
+
externalId: taskUpdate.externalId,
|
|
217
|
+
sourceMessageIds: taskUpdate.sourceMessageIds,
|
|
218
|
+
})),
|
|
219
|
+
}
|
|
220
|
+
: {}),
|
|
221
|
+
...(parsed.artifacts.length > 0 ? { artifacts: parsed.artifacts } : {}),
|
|
222
|
+
...(parsed.agentNote ? { agentNote: parsed.agentNote } : {}),
|
|
223
|
+
...(parsed.conflicts.length > 0 ? { conflicts: parsed.conflicts } : {}),
|
|
224
|
+
...(parsed.approvedBy !== null ? { approvedBy: parsed.approvedBy } : {}),
|
|
225
|
+
...(parsed.approvedAt !== null ? { approvedAt: parsed.approvedAt } : {}),
|
|
226
|
+
...(parsed.approvalMessageId !== null ? { approvalMessageId: parsed.approvalMessageId } : {}),
|
|
227
|
+
...(parsed.approvalNote !== null ? { approvalNote: parsed.approvalNote } : {}),
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export const StructuredCompactionOutputSchema = z.object({
|
|
232
|
+
summary: z.string(),
|
|
233
|
+
stateDelta: StructuredWorkstreamStateDeltaSchema,
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
export interface CompactionOutput {
|
|
237
|
+
summary: string
|
|
238
|
+
stateDelta: WorkstreamStateDelta
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export const WORKSTREAM_STATE_MAX_KEY_DECISIONS = 8
|
|
242
|
+
export const WORKSTREAM_STATE_MAX_ACTIVE_CONSTRAINTS = 6
|
|
243
|
+
export const WORKSTREAM_STATE_MAX_TASKS = 10
|
|
244
|
+
export const WORKSTREAM_STATE_MAX_OPEN_QUESTIONS = 5
|
|
245
|
+
export const WORKSTREAM_STATE_MAX_RISKS = 5
|
|
246
|
+
export const WORKSTREAM_STATE_MAX_ARTIFACTS = 10
|
|
247
|
+
export const WORKSTREAM_STATE_MAX_AGENT_CONTRIBUTIONS = 6
|
|
248
|
+
|
|
249
|
+
export function createEmptyWorkstreamState(now = Date.now()): WorkstreamState {
|
|
250
|
+
return {
|
|
251
|
+
currentPlan: null,
|
|
252
|
+
activeConstraints: [],
|
|
253
|
+
keyDecisions: [],
|
|
254
|
+
tasks: [],
|
|
255
|
+
openQuestions: [],
|
|
256
|
+
risks: [],
|
|
257
|
+
artifacts: [],
|
|
258
|
+
agentContributions: [],
|
|
259
|
+
lastUpdated: now,
|
|
260
|
+
}
|
|
261
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type { RecordIdRef } from '../db/record-id'
|
|
2
|
+
import { recordIdToString } from '../db/record-id'
|
|
3
|
+
import { TABLES } from '../db/tables'
|
|
4
|
+
import { attachmentStorageService } from '../storage/attachment-storage.service'
|
|
5
|
+
import type { UploadedWorkstreamAttachment as SdkUploadedWorkstreamAttachment } from '../storage/attachment-storage.service'
|
|
6
|
+
import type { MessagePartLike, ReadableUploadMetadata as SdkReadableUploadMetadata } from '../storage/attachments.types'
|
|
7
|
+
|
|
8
|
+
export type ReadableUploadMetadata = SdkReadableUploadMetadata
|
|
9
|
+
|
|
10
|
+
function toOrgId(orgId: RecordIdRef): string {
|
|
11
|
+
return recordIdToString(orgId, TABLES.ORGANIZATION)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function toUserId(userId: RecordIdRef): string {
|
|
15
|
+
return recordIdToString(userId, TABLES.USER)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class AttachmentService {
|
|
19
|
+
hydrateSignedFileUrlsInMessageParts({
|
|
20
|
+
parts,
|
|
21
|
+
orgId,
|
|
22
|
+
userId,
|
|
23
|
+
}: {
|
|
24
|
+
parts: readonly MessagePartLike[]
|
|
25
|
+
orgId: RecordIdRef
|
|
26
|
+
userId: RecordIdRef
|
|
27
|
+
}): MessagePartLike[] {
|
|
28
|
+
return attachmentStorageService.hydrateSignedFileUrlsInMessageParts({
|
|
29
|
+
parts,
|
|
30
|
+
orgId: toOrgId(orgId),
|
|
31
|
+
userId: toUserId(userId),
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
listReadableUploadsFromMessages({
|
|
36
|
+
messages,
|
|
37
|
+
orgId,
|
|
38
|
+
userId,
|
|
39
|
+
}: {
|
|
40
|
+
messages: ReadonlyArray<{ parts: readonly MessagePartLike[] }>
|
|
41
|
+
orgId: RecordIdRef
|
|
42
|
+
userId: RecordIdRef
|
|
43
|
+
}): ReadableUploadMetadata[] {
|
|
44
|
+
return attachmentStorageService.listReadableUploadsFromMessages({
|
|
45
|
+
messages,
|
|
46
|
+
orgId: toOrgId(orgId),
|
|
47
|
+
userId: toUserId(userId),
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async extractStoredAttachmentText({
|
|
52
|
+
storageKey,
|
|
53
|
+
name,
|
|
54
|
+
contentType,
|
|
55
|
+
}: {
|
|
56
|
+
storageKey: string
|
|
57
|
+
name: string
|
|
58
|
+
contentType: string
|
|
59
|
+
}): Promise<string> {
|
|
60
|
+
return await attachmentStorageService.extractStoredAttachmentText({ storageKey, name, contentType })
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async extractStoredAttachmentPages({
|
|
64
|
+
storageKey,
|
|
65
|
+
name,
|
|
66
|
+
contentType,
|
|
67
|
+
}: {
|
|
68
|
+
storageKey: string
|
|
69
|
+
name: string
|
|
70
|
+
contentType: string
|
|
71
|
+
}): Promise<{ pageMode: 'logical' | 'pdf'; pages: string[] }> {
|
|
72
|
+
return await attachmentStorageService.extractStoredAttachmentPages({ storageKey, name, contentType })
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async readFilePartsFromUpload({
|
|
76
|
+
upload,
|
|
77
|
+
orgId,
|
|
78
|
+
userId,
|
|
79
|
+
part,
|
|
80
|
+
pagesPerPart,
|
|
81
|
+
}: {
|
|
82
|
+
upload: ReadableUploadMetadata
|
|
83
|
+
orgId: RecordIdRef
|
|
84
|
+
userId: RecordIdRef
|
|
85
|
+
part?: number
|
|
86
|
+
pagesPerPart?: number
|
|
87
|
+
}) {
|
|
88
|
+
return await attachmentStorageService.readFilePartsFromUpload({
|
|
89
|
+
upload,
|
|
90
|
+
orgId: toOrgId(orgId),
|
|
91
|
+
userId: toUserId(userId),
|
|
92
|
+
part,
|
|
93
|
+
pagesPerPart,
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
getAttachmentUrl(storageKey: string): string {
|
|
98
|
+
return attachmentStorageService.getAttachmentUrl(storageKey)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async writeOrganizationDocument({
|
|
102
|
+
orgId,
|
|
103
|
+
namespace,
|
|
104
|
+
relativePath,
|
|
105
|
+
content,
|
|
106
|
+
contentType,
|
|
107
|
+
}: {
|
|
108
|
+
orgId: RecordIdRef
|
|
109
|
+
namespace: string
|
|
110
|
+
relativePath: string
|
|
111
|
+
content: string
|
|
112
|
+
contentType: string
|
|
113
|
+
}): Promise<{ storageKey: string; sizeBytes: number }> {
|
|
114
|
+
return await attachmentStorageService.writeOrganizationDocument({
|
|
115
|
+
orgId: toOrgId(orgId),
|
|
116
|
+
namespace,
|
|
117
|
+
relativePath,
|
|
118
|
+
content,
|
|
119
|
+
contentType,
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async uploadOrganizationDocument({
|
|
124
|
+
file,
|
|
125
|
+
orgId,
|
|
126
|
+
namespace,
|
|
127
|
+
relativePath,
|
|
128
|
+
}: {
|
|
129
|
+
file: File
|
|
130
|
+
orgId: RecordIdRef
|
|
131
|
+
namespace: string
|
|
132
|
+
relativePath: string
|
|
133
|
+
}) {
|
|
134
|
+
return await attachmentStorageService.uploadOrganizationDocument({
|
|
135
|
+
file,
|
|
136
|
+
orgId: toOrgId(orgId),
|
|
137
|
+
namespace,
|
|
138
|
+
relativePath,
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async uploadWorkstreamAttachment({
|
|
143
|
+
file,
|
|
144
|
+
orgId,
|
|
145
|
+
userId,
|
|
146
|
+
}: {
|
|
147
|
+
file: File
|
|
148
|
+
orgId: RecordIdRef
|
|
149
|
+
userId: RecordIdRef
|
|
150
|
+
}): Promise<SdkUploadedWorkstreamAttachment> {
|
|
151
|
+
return await attachmentStorageService.uploadWorkstreamAttachment({
|
|
152
|
+
file,
|
|
153
|
+
orgId: toOrgId(orgId),
|
|
154
|
+
userId: toUserId(userId),
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export const attachmentService = new AttachmentService()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ChatMessage } from '@lota-sdk/shared/schemas/chat-message'
|
|
2
|
+
|
|
3
|
+
import type { RecordIdRef } from '../db/record-id'
|
|
4
|
+
import { attachmentService } from './attachment.service'
|
|
5
|
+
import type { ReadableUploadMetadata } from './attachment.service'
|
|
6
|
+
|
|
7
|
+
export function listReadableUploadsFromChatMessages(params: {
|
|
8
|
+
messages: ChatMessage[]
|
|
9
|
+
orgId: RecordIdRef
|
|
10
|
+
userId: RecordIdRef
|
|
11
|
+
}): ReadableUploadMetadata[] {
|
|
12
|
+
return attachmentService.listReadableUploadsFromMessages({
|
|
13
|
+
messages: params.messages.map((message) => ({ parts: message.parts as Array<Record<string, unknown>> })),
|
|
14
|
+
orgId: params.orgId,
|
|
15
|
+
userId: params.userId,
|
|
16
|
+
})
|
|
17
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createWiredContextCompactionRuntime } from '../runtime/context-compaction-runtime'
|
|
2
|
+
import { createHelperModelRuntime } from '../runtime/helper-model'
|
|
3
|
+
|
|
4
|
+
const helperModelRuntime = createHelperModelRuntime()
|
|
5
|
+
|
|
6
|
+
const wiredRuntime = createWiredContextCompactionRuntime({
|
|
7
|
+
helperModelRuntime,
|
|
8
|
+
now: () => Date.now(),
|
|
9
|
+
randomId: () => Bun.randomUUIDv7(),
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
export const contextCompactionRuntime = wiredRuntime
|
|
13
|
+
export const compactMemoryBlockSummary = wiredRuntime.compactMemoryBlockSummary
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { ChatMessage } from '@lota-sdk/shared/schemas/chat-message'
|
|
2
|
+
|
|
3
|
+
import { chatLogger } from '../config/logger'
|
|
4
|
+
import type { RecordIdRef } from '../db/record-id'
|
|
5
|
+
import { recordIdToString } from '../db/record-id'
|
|
6
|
+
import { databaseService } from '../db/service'
|
|
7
|
+
import { TABLES } from '../db/tables'
|
|
8
|
+
import { parseWorkstreamState, toStateFieldsUpdated } from '../runtime/context-compaction'
|
|
9
|
+
import { CONTEXT_SIZE, WORKSTREAM_RAW_TAIL_MESSAGES } from '../runtime/context-compaction-constants'
|
|
10
|
+
import type { WorkstreamState } from '../runtime/workstream-state'
|
|
11
|
+
import { compactMemoryBlockSummary, contextCompactionRuntime } from './context-compaction-runtime'
|
|
12
|
+
import { workstreamMessageService } from './workstream-message.service'
|
|
13
|
+
import { WorkstreamSchema } from './workstream.types'
|
|
14
|
+
|
|
15
|
+
interface PersistedCompactionMetrics {
|
|
16
|
+
domain: 'workstream'
|
|
17
|
+
entityId: string
|
|
18
|
+
inputChars: number
|
|
19
|
+
outputChars: number
|
|
20
|
+
savedChars: number
|
|
21
|
+
summaryLength: number
|
|
22
|
+
compactedMessageCount: number
|
|
23
|
+
remainingMessageCount: number
|
|
24
|
+
estimatedTokens: number
|
|
25
|
+
stateFieldsUpdated: string[]
|
|
26
|
+
conflictsDetected: number
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class ContextCompactionService {
|
|
30
|
+
createSummaryMessage(summaryText: string) {
|
|
31
|
+
return contextCompactionRuntime.createSummaryMessage(summaryText)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
formatWorkstreamStateForPrompt(state: WorkstreamState | null | undefined) {
|
|
35
|
+
return contextCompactionRuntime.formatWorkstreamStateForPrompt(state)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
estimateThreshold(contextSize = CONTEXT_SIZE): number {
|
|
39
|
+
return contextCompactionRuntime.estimateThreshold(contextSize)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
shouldCompactHistory(params: { summaryText: string; liveMessages: ChatMessage[]; contextSize?: number }) {
|
|
43
|
+
return contextCompactionRuntime.shouldCompactHistory(params)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async compactWorkstreamHistory(params: {
|
|
47
|
+
workstreamId: RecordIdRef
|
|
48
|
+
contextSize?: number
|
|
49
|
+
}): Promise<{ compacted: boolean; state: WorkstreamState | null }> {
|
|
50
|
+
const workstream = await databaseService.findOne(TABLES.WORKSTREAM, { id: params.workstreamId }, WorkstreamSchema)
|
|
51
|
+
if (!workstream) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Workstream not found for compaction: ${recordIdToString(params.workstreamId, TABLES.WORKSTREAM)}`,
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const currentState = parseWorkstreamState(workstream.state)
|
|
58
|
+
const liveMessages = await workstreamMessageService.listMessagesAfterCursor(
|
|
59
|
+
params.workstreamId,
|
|
60
|
+
typeof workstream.lastCompactedMessageId === 'string' ? workstream.lastCompactedMessageId : undefined,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
const result = await contextCompactionRuntime.compactHistory({
|
|
64
|
+
summaryText: typeof workstream.chatSummary === 'string' ? workstream.chatSummary : '',
|
|
65
|
+
liveMessages,
|
|
66
|
+
tailMessageCount: WORKSTREAM_RAW_TAIL_MESSAGES,
|
|
67
|
+
contextSize: params.contextSize,
|
|
68
|
+
existingState: currentState,
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
if (!result.compacted || !result.lastCompactedMessageId) {
|
|
72
|
+
return { compacted: false, state: currentState }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (result.compactedMessages.length > 0) {
|
|
76
|
+
await workstreamMessageService.upsertMessages({
|
|
77
|
+
workstreamId: params.workstreamId,
|
|
78
|
+
messages: result.compactedMessages,
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
await databaseService.update(
|
|
83
|
+
TABLES.WORKSTREAM,
|
|
84
|
+
params.workstreamId,
|
|
85
|
+
{ chatSummary: result.summaryText, lastCompactedMessageId: result.lastCompactedMessageId, state: result.state },
|
|
86
|
+
WorkstreamSchema,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
this.logCompactionMetrics({
|
|
90
|
+
domain: 'workstream',
|
|
91
|
+
entityId: recordIdToString(params.workstreamId, TABLES.WORKSTREAM),
|
|
92
|
+
inputChars: result.inputChars,
|
|
93
|
+
outputChars: result.outputChars,
|
|
94
|
+
savedChars: Math.max(0, result.inputChars - result.outputChars),
|
|
95
|
+
summaryLength: result.summaryText.length,
|
|
96
|
+
compactedMessageCount: result.compactedMessageCount,
|
|
97
|
+
remainingMessageCount: result.remainingMessageCount,
|
|
98
|
+
estimatedTokens: result.estimatedTokens,
|
|
99
|
+
stateFieldsUpdated: toStateFieldsUpdated(result.stateDelta),
|
|
100
|
+
conflictsDetected: result.stateDelta.conflicts?.length ?? 0,
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
return { compacted: true, state: result.state }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async compactMemoryBlock(params: { previousSummary: string; newEntriesText: string }): Promise<string> {
|
|
107
|
+
return await compactMemoryBlockSummary(params)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private logCompactionMetrics(metrics: PersistedCompactionMetrics): void {
|
|
111
|
+
chatLogger.info`Persisted chat compaction applied metrics=${JSON.stringify(metrics)}`
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const contextCompactionService = new ContextCompactionService()
|