@lota-sdk/core 0.2.2 → 0.3.0
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_identity.surql +2 -2
- package/infrastructure/schema/00_thread.surql +75 -0
- package/infrastructure/schema/02_execution_plan.surql +10 -11
- package/infrastructure/schema/10_autonomous_job.surql +3 -3
- package/package.json +2 -2
- package/src/ai/definitions.ts +1 -1
- package/src/config/agent-defaults.ts +5 -5
- package/src/config/index.ts +1 -1
- package/src/config/thread-defaults.ts +72 -0
- package/src/create-runtime.ts +89 -93
- package/src/db/tables.ts +3 -3
- package/src/db/{workstream-message-row.ts → thread-message-row.ts} +3 -3
- package/src/queues/context-compaction.queue.ts +6 -6
- package/src/queues/plan-agent-heartbeat.queue.ts +3 -3
- package/src/queues/post-chat-memory.queue.ts +1 -1
- package/src/queues/title-generation.queue.ts +10 -13
- package/src/redis/index.ts +1 -1
- package/src/redis/stream-context.ts +1 -1
- package/src/runtime/agent-identity-overrides.ts +1 -1
- package/src/runtime/agent-runtime-policy.ts +19 -21
- package/src/runtime/chat-request-routing.ts +1 -1
- package/src/runtime/context-compaction-constants.ts +1 -1
- package/src/runtime/context-compaction.ts +1 -1
- package/src/runtime/execution-plan.ts +1 -1
- package/src/runtime/index.ts +1 -1
- package/src/runtime/memory-digest-policy.ts +1 -1
- package/src/runtime/plugin-types.ts +1 -1
- package/src/runtime/post-turn-side-effects.ts +35 -35
- package/src/runtime/runtime-config.ts +12 -12
- package/src/runtime/runtime-extensions.ts +11 -11
- package/src/runtime/social-chat-agent-runner.ts +3 -3
- package/src/runtime/social-chat-history.ts +1 -1
- package/src/runtime/social-chat.ts +6 -6
- package/src/runtime/team-consultation-orchestrator.ts +1 -1
- package/src/runtime/{workstream-chat-helpers.ts → thread-chat-helpers.ts} +7 -7
- package/src/runtime/{workstream-plan-turn.ts → thread-plan-turn.ts} +11 -17
- package/src/runtime/{workstream-turn-context.ts → thread-turn-context.ts} +10 -10
- package/src/services/agent-activity.service.ts +39 -44
- package/src/services/agent-executor.service.ts +17 -19
- package/src/services/attachment.service.ts +4 -8
- package/src/services/autonomous-job.service.ts +29 -28
- package/src/services/context-compaction.service.ts +19 -29
- package/src/services/execution-plan.service.ts +58 -70
- package/src/services/global-orchestrator.service.ts +5 -5
- package/src/services/index.ts +6 -6
- package/src/services/memory.service.ts +1 -1
- package/src/services/monitoring-window.service.ts +2 -2
- package/src/services/mutating-approval.service.ts +7 -10
- package/src/services/node-workspace.service.ts +8 -7
- package/src/services/notification.service.ts +1 -1
- package/src/services/organization.service.ts +9 -9
- package/src/services/ownership-dispatcher.service.ts +13 -19
- package/src/services/plan-agent-heartbeat.service.ts +13 -13
- package/src/services/plan-agent-query.service.ts +7 -7
- package/src/services/plan-artifact.service.ts +1 -2
- package/src/services/plan-coordination.service.ts +4 -4
- package/src/services/plan-cycle.service.ts +7 -7
- package/src/services/plan-deadline.service.ts +4 -4
- package/src/services/plan-event-delivery.service.ts +8 -12
- package/src/services/plan-executor.service.ts +16 -37
- package/src/services/plan-run-data.ts +27 -8
- package/src/services/plan-run.service.ts +7 -9
- package/src/services/plan-scheduler.service.ts +4 -4
- package/src/services/plan-template.service.ts +2 -2
- package/src/services/plan-validator.service.ts +0 -11
- package/src/services/plugin-executor.service.ts +1 -1
- package/src/services/queue-job.service.ts +1 -1
- package/src/services/recent-activity-title.service.ts +1 -1
- package/src/services/recent-activity.service.ts +4 -4
- package/src/services/system-executor.service.ts +2 -2
- package/src/services/{workstream-message.service.ts → thread-message.service.ts} +72 -76
- package/src/services/thread-plan-registry.service.ts +22 -0
- package/src/services/thread-title.service.ts +39 -0
- package/src/services/{workstream-turn-preparation.service.ts → thread-turn-preparation.service.ts} +131 -143
- package/src/services/{workstream-turn.ts → thread-turn.ts} +27 -31
- package/src/services/thread.service.ts +707 -0
- package/src/services/thread.types.ts +17 -0
- package/src/storage/attachment-storage.service.ts +4 -4
- package/src/system-agents/index.ts +1 -1
- package/src/system-agents/memory.agent.ts +1 -1
- package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
- package/src/system-agents/regular-chat-memory-digest.agent.ts +1 -1
- package/src/system-agents/researcher.agent.ts +3 -3
- package/src/system-agents/{workstream-router.agent.ts → thread-router.agent.ts} +21 -21
- package/src/system-agents/title-generator.agent.ts +8 -8
- package/src/tools/execution-plan.tool.ts +39 -40
- package/src/tools/memory-block.tool.ts +4 -4
- package/src/tools/research-topic.tool.ts +1 -0
- package/src/tools/search-web.tool.ts +1 -1
- package/src/tools/search.tool.ts +4 -4
- package/src/tools/team-think.tool.ts +9 -9
- package/src/workers/regular-chat-memory-digest.helpers.ts +1 -1
- package/src/workers/regular-chat-memory-digest.runner.ts +43 -43
- package/src/workers/skill-extraction.runner.ts +9 -13
- package/src/workers/utils/{workstream-message-query.ts → thread-message-query.ts} +21 -21
- package/infrastructure/schema/00_workstream.surql +0 -64
- package/src/config/workstream-defaults.ts +0 -72
- package/src/services/workstream-plan-registry.service.ts +0 -22
- package/src/services/workstream-title.service.ts +0 -42
- package/src/services/workstream.service.ts +0 -803
- package/src/services/workstream.types.ts +0 -17
- /package/src/services/{workstream-constants.ts → thread-constants.ts} +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { sdkPublicThreadSchema, sdkThreadRecordSchema, sdkThreadSchema } from '@lota-sdk/shared'
|
|
2
|
+
import type { SdkPublicThread, SdkThreadRecord } from '@lota-sdk/shared'
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
|
|
5
|
+
export const ThreadSchema = sdkThreadRecordSchema
|
|
6
|
+
export type ThreadRecord = SdkThreadRecord
|
|
7
|
+
|
|
8
|
+
export const NormalizedThreadSchema = sdkThreadSchema.extend({
|
|
9
|
+
agentId: z.string().optional(),
|
|
10
|
+
threadType: z.string().optional(),
|
|
11
|
+
memoryBlock: z.string(),
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
export type NormalizedThread = z.infer<typeof NormalizedThreadSchema>
|
|
15
|
+
export type PublicThread = SdkPublicThread
|
|
16
|
+
|
|
17
|
+
export { sdkPublicThreadSchema as PublicThreadSchema }
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
|
|
27
27
|
const READ_FILE_PARTS_PAGES_PER_PART = 25
|
|
28
28
|
|
|
29
|
-
export type
|
|
29
|
+
export type UploadedThreadAttachment = {
|
|
30
30
|
filename: string
|
|
31
31
|
mediaType: string
|
|
32
32
|
sizeBytes: number
|
|
@@ -86,7 +86,7 @@ export class AttachmentStorageService {
|
|
|
86
86
|
orgId: string
|
|
87
87
|
namespace: string
|
|
88
88
|
relativePath: string
|
|
89
|
-
}): Promise<
|
|
89
|
+
}): Promise<UploadedThreadAttachment> {
|
|
90
90
|
const filename = file.name || 'document'
|
|
91
91
|
const mediaType = file.type || inferContentType(filename)
|
|
92
92
|
const storageKey = buildOrganizationDocumentStorageKey({
|
|
@@ -107,7 +107,7 @@ export class AttachmentStorageService {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
async
|
|
110
|
+
async uploadThreadAttachment({
|
|
111
111
|
file,
|
|
112
112
|
orgId,
|
|
113
113
|
userId,
|
|
@@ -115,7 +115,7 @@ export class AttachmentStorageService {
|
|
|
115
115
|
file: File
|
|
116
116
|
orgId: string
|
|
117
117
|
userId: string
|
|
118
|
-
}): Promise<
|
|
118
|
+
}): Promise<UploadedThreadAttachment> {
|
|
119
119
|
const filename = file.name || 'attachment'
|
|
120
120
|
const mediaType = file.type || inferContentType(filename)
|
|
121
121
|
const sizeBytes = file.size
|
|
@@ -27,7 +27,7 @@ Turn recent activity context into a short label that helps the user quickly reco
|
|
|
27
27
|
- Prefer concrete task language over generic labels.
|
|
28
28
|
- Mention the agent only when it adds useful context.
|
|
29
29
|
- Focus on what the user was trying to accomplish.
|
|
30
|
-
- Avoid generic phrases like "Chat", "Conversation", "Recent activity", "Task", or "
|
|
30
|
+
- Avoid generic phrases like "Chat", "Conversation", "Recent activity", "Task", or "Thread update".
|
|
31
31
|
- Avoid punctuation at the end.
|
|
32
32
|
- Do not invent details that are not present in the input.
|
|
33
33
|
- If the input is too weak for improvement, return the current system title.
|
|
@@ -58,7 +58,7 @@ Turn recent activity context into a short label that helps the user quickly reco
|
|
|
58
58
|
- Prefer concrete task language over generic labels.
|
|
59
59
|
- Mention the agent only when it adds useful context.
|
|
60
60
|
- Focus on what the user was trying to accomplish.
|
|
61
|
-
- Avoid generic phrases like "Chat", "Conversation", "Recent activity", "Task", or "
|
|
61
|
+
- Avoid generic phrases like "Chat", "Conversation", "Recent activity", "Task", or "Thread update".
|
|
62
62
|
- Avoid punctuation at the end.
|
|
63
63
|
- Do not invent details that are not present in the input.
|
|
64
64
|
- If the input is too weak for improvement, return the current system title.
|
|
@@ -16,7 +16,7 @@ Synthesize an updated workspace profile summary and durable memory facts from co
|
|
|
16
16
|
|
|
17
17
|
<rules>
|
|
18
18
|
- Evidence-grounded only. Do not invent details. Exclude routing/tool chatter.
|
|
19
|
-
- Treat [
|
|
19
|
+
- Treat [thread:...] prefixes as thread context only.
|
|
20
20
|
- Preserve existing profile format. Merge corrections; remove stale claims only when contradicted.
|
|
21
21
|
- Facts must be standalone, one concrete claim each, understandable without transcript context.
|
|
22
22
|
- If no durable updates exist, return current summary unchanged and empty facts.
|
|
@@ -2,12 +2,12 @@ export const RESEARCHER_PROMPT = `<agent-instructions>
|
|
|
2
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
3
|
|
|
4
4
|
<workflow>
|
|
5
|
-
1. Break the research task into 2-
|
|
5
|
+
1. Break the research task into 2-3 focused search queries.
|
|
6
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
7
|
3. Run all searchWeb calls in parallel.
|
|
8
|
-
4. Review results and identify the 3
|
|
8
|
+
4. Review results and identify the 2-3 most authoritative/relevant URLs.
|
|
9
9
|
5. Fetch those pages in parallel using fetchWebpage.
|
|
10
|
-
6. If initial results are insufficient, reformulate queries and
|
|
10
|
+
6. If initial results are insufficient, reformulate 1-2 queries and retry once.
|
|
11
11
|
7. Synthesize findings into a structured markdown report.
|
|
12
12
|
</workflow>
|
|
13
13
|
|
|
@@ -170,14 +170,14 @@ function extractResultText(result: { text?: string; reasoning?: unknown }): stri
|
|
|
170
170
|
function logRouterRaw(label: 'triage' | 'check', text: string): void {
|
|
171
171
|
const preview = text.trim().slice(0, ROUTER_OUTPUT_PREVIEW_CHARS)
|
|
172
172
|
if (!preview) return
|
|
173
|
-
console.log(`[
|
|
173
|
+
console.log(`[thread-router] ${label} raw:`, preview)
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
// ---------------------------------------------------------------------------
|
|
177
177
|
// Prompts
|
|
178
178
|
// ---------------------------------------------------------------------------
|
|
179
179
|
|
|
180
|
-
const TRIAGE_SYSTEM_PROMPT = `You are a
|
|
180
|
+
const TRIAGE_SYSTEM_PROMPT = `You are a thread message router. Decide which team member should respond FIRST to the user message.
|
|
181
181
|
|
|
182
182
|
Rules:
|
|
183
183
|
- Pick the single best-fit agent from the members list based on domain expertise.
|
|
@@ -211,17 +211,17 @@ Format: {"done":true} or {"done":false,"agentId":"<id>","routingContext":"<1-sen
|
|
|
211
211
|
function createRouterAgent(systemPrompt: string) {
|
|
212
212
|
const modelId = routerModelId ?? 'openai/gpt-5.4-nano'
|
|
213
213
|
return new ToolLoopAgent({
|
|
214
|
-
id: '
|
|
214
|
+
id: 'thread-router',
|
|
215
215
|
model: aiGatewayChatModel(modelId),
|
|
216
|
-
headers: buildAiGatewayDirectCacheHeaders('
|
|
216
|
+
headers: buildAiGatewayDirectCacheHeaders('thread-router'),
|
|
217
217
|
providerOptions: { openai: { reasoningEffort: 'low' } },
|
|
218
218
|
instructions: systemPrompt,
|
|
219
219
|
maxOutputTokens: 256,
|
|
220
220
|
})
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
export async function
|
|
224
|
-
|
|
223
|
+
export async function triageThreadMessage(params: {
|
|
224
|
+
threadTitle: string
|
|
225
225
|
members: readonly string[]
|
|
226
226
|
messageText: string
|
|
227
227
|
recentContext?: string
|
|
@@ -245,7 +245,7 @@ export async function triageWorkstreamMessage(params: {
|
|
|
245
245
|
|
|
246
246
|
const membersDesc = buildMembersDescription(params.members, displayOptions)
|
|
247
247
|
const prompt = [
|
|
248
|
-
`
|
|
248
|
+
`Thread: "${params.threadTitle}"`,
|
|
249
249
|
`Members:\n${membersDesc}`,
|
|
250
250
|
params.recentContext ? `Recent context:\n${params.recentContext}` : '',
|
|
251
251
|
`User message: "${params.messageText}"`,
|
|
@@ -258,7 +258,7 @@ export async function triageWorkstreamMessage(params: {
|
|
|
258
258
|
try {
|
|
259
259
|
result = await agent.generate({ messages: [{ role: 'user', content: prompt }], timeout: { totalMs: 30_000 } })
|
|
260
260
|
} catch (error) {
|
|
261
|
-
console.error('[
|
|
261
|
+
console.error('[thread-router] triage failed:', error instanceof Error ? error.message : error)
|
|
262
262
|
return null
|
|
263
263
|
}
|
|
264
264
|
|
|
@@ -267,30 +267,30 @@ export async function triageWorkstreamMessage(params: {
|
|
|
267
267
|
const json = extractJson(effectiveText)
|
|
268
268
|
if (json === null) {
|
|
269
269
|
if (effectiveText.trim()) {
|
|
270
|
-
console.log('[
|
|
270
|
+
console.log('[thread-router] triage ignored non-json output')
|
|
271
271
|
}
|
|
272
272
|
return null
|
|
273
273
|
}
|
|
274
274
|
const parsed = TriageResultSchema.safeParse(json)
|
|
275
275
|
if (!parsed.success) {
|
|
276
|
-
console.log('[
|
|
276
|
+
console.log('[thread-router] triage parse failed:', JSON.stringify(parsed.error.issues))
|
|
277
277
|
return null
|
|
278
278
|
}
|
|
279
279
|
if (!parsed.data.agentId) {
|
|
280
|
-
console.log('[
|
|
280
|
+
console.log('[thread-router] triage returned empty agentId — fallback to owner')
|
|
281
281
|
return null
|
|
282
282
|
}
|
|
283
283
|
if (!params.members.includes(parsed.data.agentId)) {
|
|
284
|
-
console.log('[
|
|
284
|
+
console.log('[thread-router] triage returned unknown agent:', parsed.data.agentId)
|
|
285
285
|
return null
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
-
console.log('[
|
|
288
|
+
console.log('[thread-router] triage routed to:', parsed.data.agentId)
|
|
289
289
|
return parsed.data
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
export async function checkForNextAgent(params: {
|
|
293
|
-
|
|
293
|
+
threadTitle: string
|
|
294
294
|
members: readonly string[]
|
|
295
295
|
messageText: string
|
|
296
296
|
respondedAgents: string[]
|
|
@@ -321,7 +321,7 @@ export async function checkForNextAgent(params: {
|
|
|
321
321
|
const respondedList = params.respondedAgents.map((id) => readDisplayName(id, displayOptions)).join(', ')
|
|
322
322
|
|
|
323
323
|
const prompt = [
|
|
324
|
-
`
|
|
324
|
+
`Thread: "${params.threadTitle}"`,
|
|
325
325
|
`Remaining members:\n${membersDesc}`,
|
|
326
326
|
`Already responded: ${respondedList}`,
|
|
327
327
|
`User message: "${params.messageText}"`,
|
|
@@ -333,7 +333,7 @@ export async function checkForNextAgent(params: {
|
|
|
333
333
|
try {
|
|
334
334
|
result = await agent.generate({ messages: [{ role: 'user', content: prompt }], timeout: { totalMs: 30_000 } })
|
|
335
335
|
} catch (error) {
|
|
336
|
-
console.error('[
|
|
336
|
+
console.error('[thread-router] check failed:', error instanceof Error ? error.message : error)
|
|
337
337
|
return { done: true }
|
|
338
338
|
}
|
|
339
339
|
|
|
@@ -342,24 +342,24 @@ export async function checkForNextAgent(params: {
|
|
|
342
342
|
const json = extractJson(effectiveText)
|
|
343
343
|
if (json === null) {
|
|
344
344
|
if (effectiveText.trim()) {
|
|
345
|
-
console.log('[
|
|
345
|
+
console.log('[thread-router] check ignored non-json output')
|
|
346
346
|
}
|
|
347
347
|
return { done: true }
|
|
348
348
|
}
|
|
349
349
|
const parsed = CheckResultSchema.safeParse(json)
|
|
350
350
|
if (!parsed.success) {
|
|
351
|
-
console.log('[
|
|
351
|
+
console.log('[thread-router] check parse failed:', JSON.stringify(parsed.error.issues))
|
|
352
352
|
return { done: true }
|
|
353
353
|
}
|
|
354
354
|
if (parsed.data.done) {
|
|
355
|
-
console.log('[
|
|
355
|
+
console.log('[thread-router] check: done, no more agents needed')
|
|
356
356
|
return { done: true }
|
|
357
357
|
}
|
|
358
358
|
if (!parsed.data.agentId || !remainingMembers.includes(parsed.data.agentId)) {
|
|
359
|
-
console.log('[
|
|
359
|
+
console.log('[thread-router] check: invalid agentId:', parsed.data.agentId)
|
|
360
360
|
return { done: true }
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
-
console.log('[
|
|
363
|
+
console.log('[thread-router] check: next agent:', parsed.data.agentId)
|
|
364
364
|
return parsed.data
|
|
365
365
|
}
|
|
@@ -9,9 +9,9 @@ import {
|
|
|
9
9
|
import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
|
|
10
10
|
import { resolveHelperAgentOptions } from './helper-agent-options'
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const THREAD_TITLE_MAX_TOKENS = 512
|
|
13
13
|
|
|
14
|
-
export const
|
|
14
|
+
export const THREAD_TITLE_GENERATOR_PROMPT = `<agent-instructions>
|
|
15
15
|
You are a **Title Generator** that creates concise chat titles.
|
|
16
16
|
|
|
17
17
|
<task>
|
|
@@ -20,7 +20,7 @@ Generate a chat title based only on the user's message.
|
|
|
20
20
|
|
|
21
21
|
<constraints>
|
|
22
22
|
- Maximum 3-4 words
|
|
23
|
-
- Capture the core
|
|
23
|
+
- Capture the core thread or intent
|
|
24
24
|
- Use natural, readable language
|
|
25
25
|
- No punctuation at the end
|
|
26
26
|
</constraints>
|
|
@@ -30,15 +30,15 @@ Return only the title text. No quotes, no labels, no explanation.
|
|
|
30
30
|
</output-format>
|
|
31
31
|
</agent-instructions>`
|
|
32
32
|
|
|
33
|
-
export function
|
|
33
|
+
export function createThreadTitleGeneratorAgent(options: CreateHelperToolLoopAgentOptions) {
|
|
34
34
|
return new ToolLoopAgent({
|
|
35
|
-
id: '
|
|
35
|
+
id: 'thread-title-generator',
|
|
36
36
|
model: aiGatewayModel(OPENROUTER_FAST_REASONING_MODEL_ID),
|
|
37
|
-
headers: buildAiGatewayDirectCacheHeaders('
|
|
37
|
+
headers: buildAiGatewayDirectCacheHeaders('thread-title-generator'),
|
|
38
38
|
providerOptions: OPENROUTER_MINIMAL_REASONING_PROVIDER_OPTIONS,
|
|
39
39
|
...resolveHelperAgentOptions(options, {
|
|
40
|
-
instructions:
|
|
41
|
-
maxOutputTokens:
|
|
40
|
+
instructions: THREAD_TITLE_GENERATOR_PROMPT,
|
|
41
|
+
maxOutputTokens: THREAD_TITLE_MAX_TOKENS,
|
|
42
42
|
}),
|
|
43
43
|
})
|
|
44
44
|
}
|
|
@@ -5,19 +5,16 @@ import {
|
|
|
5
5
|
expandAgentPlanDraft,
|
|
6
6
|
getLatestExecutionPlanResult,
|
|
7
7
|
} from '@lota-sdk/shared'
|
|
8
|
-
import type { ExecutionPlanAction, ExecutionPlanArgs
|
|
8
|
+
import type { CreateProjectWithPlanResultData, ExecutionPlanAction, ExecutionPlanArgs } from '@lota-sdk/shared'
|
|
9
9
|
import { tool } from 'ai'
|
|
10
10
|
|
|
11
11
|
import type { RecordIdRef } from '../db/record-id'
|
|
12
12
|
import { recordIdToString } from '../db/record-id'
|
|
13
13
|
import { TABLES } from '../db/tables'
|
|
14
14
|
import { executionPlanService } from '../services/execution-plan.service'
|
|
15
|
-
import {
|
|
15
|
+
import { threadService } from '../services/thread.service'
|
|
16
16
|
|
|
17
|
-
type
|
|
18
|
-
typeof workstreamService,
|
|
19
|
-
'createWorkstream' | 'deleteWorkstream' | 'getWorkstream'
|
|
20
|
-
>
|
|
17
|
+
type ExecutionPlanThreadService = Pick<typeof threadService, 'createThread' | 'deleteThread' | 'getThread'>
|
|
21
18
|
|
|
22
19
|
type ExecutionPlanExecutionPlanService = Pick<
|
|
23
20
|
typeof executionPlanService,
|
|
@@ -26,90 +23,92 @@ type ExecutionPlanExecutionPlanService = Pick<
|
|
|
26
23
|
| 'resumeRun'
|
|
27
24
|
| 'listActivePlanSummaries'
|
|
28
25
|
| 'getActivePlanToolResult'
|
|
29
|
-
| '
|
|
26
|
+
| 'getActivePlansForThread'
|
|
30
27
|
>
|
|
31
28
|
|
|
32
29
|
function extractDraft(input: ExecutionPlanArgs) {
|
|
33
|
-
const { action, projectTitle,
|
|
34
|
-
return { action, projectTitle,
|
|
30
|
+
const { action, projectTitle, targetThreadId, runId, reason, ...draftInput } = input
|
|
31
|
+
return { action, projectTitle, targetThreadId, runId, reason, draft: expandAgentPlanDraft(draftInput) }
|
|
35
32
|
}
|
|
36
33
|
|
|
37
34
|
export function createExecutionPlanTool(params: {
|
|
38
35
|
orgId: RecordIdRef
|
|
39
36
|
userId: RecordIdRef
|
|
40
|
-
|
|
37
|
+
threadId: RecordIdRef
|
|
41
38
|
agentId: string
|
|
42
39
|
executionPlanService?: ExecutionPlanExecutionPlanService
|
|
43
|
-
|
|
40
|
+
threadService?: ExecutionPlanThreadService
|
|
44
41
|
onPlanChanged?: () => void
|
|
45
42
|
validateInlinePlan?: (draft: ReturnType<typeof expandAgentPlanDraft>) => void
|
|
46
43
|
}) {
|
|
47
44
|
const resolvedEpService = params.executionPlanService ?? executionPlanService
|
|
48
|
-
const resolvedWsService = params.
|
|
45
|
+
const resolvedWsService = params.threadService ?? threadService
|
|
49
46
|
|
|
50
47
|
return tool({
|
|
51
48
|
description:
|
|
52
|
-
'Manage execution plans. Actions: create (inline, 1-2 nodes), create-project (dedicated project
|
|
49
|
+
'Manage execution plans. Actions: create (inline, 1-2 nodes), create-project (dedicated project thread, 3+ nodes), replace (swap active plan), resume (resume interrupted plan).',
|
|
53
50
|
inputSchema: ExecutionPlanArgsSchema,
|
|
54
51
|
execute: async (input) => {
|
|
55
|
-
const { action, projectTitle,
|
|
52
|
+
const { action, projectTitle, targetThreadId, runId, reason, draft } = extractDraft(input)
|
|
56
53
|
|
|
57
54
|
const handler: Record<ExecutionPlanAction, () => Promise<unknown>> = {
|
|
58
55
|
create: async () => {
|
|
59
56
|
params.validateInlinePlan?.(draft)
|
|
60
57
|
return await resolvedEpService.createPlan({
|
|
61
58
|
organizationId: params.orgId,
|
|
62
|
-
|
|
59
|
+
threadId: targetThreadId ?? params.threadId,
|
|
63
60
|
leadAgentId: params.agentId,
|
|
64
61
|
input: draft,
|
|
65
62
|
})
|
|
66
63
|
},
|
|
67
64
|
|
|
68
65
|
'create-project': async () => {
|
|
69
|
-
const
|
|
70
|
-
? await resolvedWsService.
|
|
66
|
+
const targetThread = targetThreadId
|
|
67
|
+
? await resolvedWsService.getThread(targetThreadId)
|
|
71
68
|
: await (() => {
|
|
72
69
|
if (!projectTitle) {
|
|
73
70
|
throw new Error('projectTitle is required when action is "create-project".')
|
|
74
71
|
}
|
|
75
72
|
|
|
76
|
-
return resolvedWsService.
|
|
73
|
+
return resolvedWsService.createThread({
|
|
74
|
+
userId: params.userId,
|
|
75
|
+
organizationId: params.orgId,
|
|
77
76
|
title: projectTitle,
|
|
78
|
-
|
|
77
|
+
type: 'group',
|
|
79
78
|
})
|
|
80
79
|
})()
|
|
81
80
|
|
|
82
|
-
if (
|
|
83
|
-
throw new Error('Target
|
|
81
|
+
if (targetThread.organizationId !== recordIdToString(params.orgId, TABLES.ORGANIZATION)) {
|
|
82
|
+
throw new Error('Target thread belongs to a different organization.')
|
|
84
83
|
}
|
|
85
|
-
if (
|
|
86
|
-
throw new Error('Target
|
|
84
|
+
if (targetThread.userId !== recordIdToString(params.userId, TABLES.USER)) {
|
|
85
|
+
throw new Error('Target thread belongs to a different user.')
|
|
87
86
|
}
|
|
88
87
|
|
|
89
|
-
const existingPlans = await resolvedEpService.
|
|
90
|
-
if (
|
|
88
|
+
const existingPlans = await resolvedEpService.getActivePlansForThread(targetThread.id)
|
|
89
|
+
if (targetThread.type !== 'thread' && existingPlans.length > 0) {
|
|
91
90
|
throw new Error(
|
|
92
|
-
'This
|
|
91
|
+
'This thread already has an active execution plan. Use action "replace" or target a core thread.',
|
|
93
92
|
)
|
|
94
93
|
}
|
|
95
94
|
|
|
96
|
-
const
|
|
95
|
+
const createdThread = !targetThreadId
|
|
97
96
|
try {
|
|
98
97
|
const result = await resolvedEpService.createPlan({
|
|
99
98
|
organizationId: params.orgId,
|
|
100
|
-
|
|
99
|
+
threadId: targetThread.id,
|
|
101
100
|
leadAgentId: params.agentId,
|
|
102
101
|
input: draft,
|
|
103
102
|
})
|
|
104
103
|
return {
|
|
105
104
|
...result,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
threadId: targetThread.id,
|
|
106
|
+
threadTitle: targetThread.title,
|
|
107
|
+
createdThread,
|
|
109
108
|
} satisfies CreateProjectWithPlanResultData
|
|
110
109
|
} catch (error) {
|
|
111
|
-
if (
|
|
112
|
-
await resolvedWsService.
|
|
110
|
+
if (createdThread) {
|
|
111
|
+
await resolvedWsService.deleteThread(targetThread.id).catch(() => {})
|
|
113
112
|
}
|
|
114
113
|
throw error
|
|
115
114
|
}
|
|
@@ -122,7 +121,7 @@ export function createExecutionPlanTool(params: {
|
|
|
122
121
|
|
|
123
122
|
return await resolvedEpService.replacePlan({
|
|
124
123
|
organizationId: params.orgId,
|
|
125
|
-
|
|
124
|
+
threadId: params.threadId,
|
|
126
125
|
leadAgentId: params.agentId,
|
|
127
126
|
input: { runId, reason, ...draft },
|
|
128
127
|
})
|
|
@@ -134,7 +133,7 @@ export function createExecutionPlanTool(params: {
|
|
|
134
133
|
}
|
|
135
134
|
|
|
136
135
|
return await resolvedEpService.resumeRun({
|
|
137
|
-
|
|
136
|
+
threadId: params.threadId,
|
|
138
137
|
emittedBy: params.agentId,
|
|
139
138
|
input: { runId },
|
|
140
139
|
})
|
|
@@ -148,17 +147,17 @@ export function createExecutionPlanTool(params: {
|
|
|
148
147
|
})
|
|
149
148
|
}
|
|
150
149
|
|
|
151
|
-
export function createExecutionPlanQueryTool(params: {
|
|
150
|
+
export function createExecutionPlanQueryTool(params: { threadId: RecordIdRef }) {
|
|
152
151
|
return tool({
|
|
153
152
|
description:
|
|
154
153
|
'Query execution plans. Omit runId to list all active plans. Provide runId to load a specific plan run.',
|
|
155
154
|
inputSchema: ExecutionPlanQueryArgsSchema,
|
|
156
155
|
execute: async (input) => {
|
|
157
156
|
if (!input.runId) {
|
|
158
|
-
return await executionPlanService.listActivePlanSummaries(params.
|
|
157
|
+
return await executionPlanService.listActivePlanSummaries(params.threadId)
|
|
159
158
|
}
|
|
160
159
|
return await executionPlanService.getActivePlanToolResult({
|
|
161
|
-
|
|
160
|
+
threadId: params.threadId,
|
|
162
161
|
runId: input.runId,
|
|
163
162
|
includeEvents: input.includeEvents,
|
|
164
163
|
includeArtifacts: input.includeArtifacts,
|
|
@@ -171,7 +170,7 @@ export function createExecutionPlanQueryTool(params: { workstreamId: RecordIdRef
|
|
|
171
170
|
}
|
|
172
171
|
|
|
173
172
|
export function createSubmitExecutionNodeResultTool(params: {
|
|
174
|
-
|
|
173
|
+
threadId: RecordIdRef
|
|
175
174
|
agentId: string
|
|
176
175
|
onPlanChanged?: () => void
|
|
177
176
|
}) {
|
|
@@ -181,7 +180,7 @@ export function createSubmitExecutionNodeResultTool(params: {
|
|
|
181
180
|
inputSchema: SubmitExecutionNodeResultArgsSchema,
|
|
182
181
|
execute: async (input) => {
|
|
183
182
|
const result = await executionPlanService.submitNodeResult({
|
|
184
|
-
|
|
183
|
+
threadId: params.threadId,
|
|
185
184
|
emittedBy: params.agentId,
|
|
186
185
|
input,
|
|
187
186
|
})
|
|
@@ -8,16 +8,16 @@ import {
|
|
|
8
8
|
prepareMemoryBlockAppend,
|
|
9
9
|
validateMemoryBlockEntry,
|
|
10
10
|
} from '../runtime/memory-block'
|
|
11
|
-
import {
|
|
11
|
+
import { threadService } from '../services/thread.service'
|
|
12
12
|
import { safeEnqueue } from '../utils/async'
|
|
13
13
|
|
|
14
14
|
export function createMemoryBlockTool({
|
|
15
|
-
|
|
15
|
+
threadId,
|
|
16
16
|
agentLabel,
|
|
17
17
|
getCurrentBlock,
|
|
18
18
|
onAppend,
|
|
19
19
|
}: {
|
|
20
|
-
|
|
20
|
+
threadId: RecordIdRef
|
|
21
21
|
agentLabel: string
|
|
22
22
|
getCurrentBlock?: () => string
|
|
23
23
|
onAppend?: (value: string) => void
|
|
@@ -43,7 +43,7 @@ export function createMemoryBlockTool({
|
|
|
43
43
|
|
|
44
44
|
void safeEnqueue(
|
|
45
45
|
async () => {
|
|
46
|
-
const updated = await
|
|
46
|
+
const updated = await threadService.appendMemoryBlock(threadId, prepared.formatted)
|
|
47
47
|
onAppend?.(updated)
|
|
48
48
|
},
|
|
49
49
|
{ operationName: 'append memory block entry', logPrefix: 'Background memoryBlockAppend task failed' },
|
|
@@ -18,4 +18,5 @@ export const researchTopicTool = createDelegatedAgentTool({
|
|
|
18
18
|
headers: buildAiGatewayStrictSemanticCacheHeaders('researchTopic'),
|
|
19
19
|
instructions: RESEARCHER_PROMPT,
|
|
20
20
|
tools: { searchWeb: searchWebTool.create(), fetchWebpage: fetchWebpageTool.create() },
|
|
21
|
+
maxSteps: 6,
|
|
21
22
|
})
|
|
@@ -139,7 +139,7 @@ export const searchWebTool = {
|
|
|
139
139
|
inputSchema: z
|
|
140
140
|
.object({
|
|
141
141
|
query: z.string().min(1, 'Query is required'),
|
|
142
|
-
limit: z.number().int().min(1).max(10).
|
|
142
|
+
limit: z.number().int().min(1).max(10).default(3),
|
|
143
143
|
sources: z.array(SourceSchema).optional(),
|
|
144
144
|
location: z.string().optional(),
|
|
145
145
|
tbs: z.string().optional(),
|
package/src/tools/search.tool.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { z } from 'zod'
|
|
|
4
4
|
import { isAgentName } from '../config/agent-defaults'
|
|
5
5
|
import type { RecordIdRef } from '../db/record-id'
|
|
6
6
|
import { memoryService } from '../services/memory.service'
|
|
7
|
-
import {
|
|
7
|
+
import { threadMessageService } from '../services/thread-message.service'
|
|
8
8
|
|
|
9
9
|
const CONVERSATION_SEARCH_RESULT_LIMIT = 20
|
|
10
10
|
const MemorySearchInputSchema = z.object({ query: z.string().min(1) }).strict()
|
|
@@ -36,14 +36,14 @@ export function createMemorySearchTool(
|
|
|
36
36
|
})
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
export function createConversationSearchTool(
|
|
39
|
+
export function createConversationSearchTool(threadId: RecordIdRef) {
|
|
40
40
|
return tool({
|
|
41
41
|
description: 'Search prior chat messages by role and query text.',
|
|
42
42
|
inputSchema: ConversationSearchInputSchema,
|
|
43
43
|
execute: async ({ query, type }: z.infer<typeof ConversationSearchInputSchema>) => {
|
|
44
44
|
const normalizedQuery = query.trim()
|
|
45
|
-
const results = await
|
|
46
|
-
|
|
45
|
+
const results = await threadMessageService.searchMessages({
|
|
46
|
+
threadId,
|
|
47
47
|
role: type === 'user' ? 'user' : 'assistant',
|
|
48
48
|
query: normalizedQuery,
|
|
49
49
|
limit: CONVERSATION_SEARCH_RESULT_LIMIT,
|
|
@@ -14,7 +14,7 @@ import type { LotaRuntimeTeamThinkToolsParams } from '../runtime/runtime-extensi
|
|
|
14
14
|
import { createConsultTeamTool as createConsultTeamToolSdk } from '../runtime/team-consultation-orchestrator'
|
|
15
15
|
import type { DefaultRepoSections, TeamConsultationParticipantRunner } from '../runtime/team-consultation-orchestrator'
|
|
16
16
|
import { buildTeamConsultationResponseGuard } from '../runtime/team-consultation-prompts'
|
|
17
|
-
import { asRecord, readInstructionSections, readOptionalString } from '../runtime/
|
|
17
|
+
import { asRecord, readInstructionSections, readOptionalString } from '../runtime/thread-chat-helpers'
|
|
18
18
|
import type { ReadableUploadMetadata } from '../services/attachment.service'
|
|
19
19
|
|
|
20
20
|
async function buildTeamThinkAgentTools(
|
|
@@ -37,7 +37,7 @@ export function createTeamThinkTool(params: {
|
|
|
37
37
|
latestUserMessageId: string
|
|
38
38
|
orgId: RecordIdRef
|
|
39
39
|
userId: RecordIdRef
|
|
40
|
-
|
|
40
|
+
threadId: RecordIdRef
|
|
41
41
|
githubInstalled: boolean
|
|
42
42
|
availableUploads: ReadableUploadMetadata[]
|
|
43
43
|
provideRepoTool: boolean
|
|
@@ -60,9 +60,9 @@ export function createTeamThinkTool(params: {
|
|
|
60
60
|
const agentResolution = asRecord(
|
|
61
61
|
await getTurnHooks().resolveAgent?.({
|
|
62
62
|
agentId,
|
|
63
|
-
mode: '
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
mode: 'fixedThreadMode',
|
|
64
|
+
thread: null,
|
|
65
|
+
threadRef: params.threadId,
|
|
66
66
|
orgRef: params.orgId,
|
|
67
67
|
userRef: params.userId,
|
|
68
68
|
onboardingActive: false,
|
|
@@ -78,8 +78,8 @@ export function createTeamThinkTool(params: {
|
|
|
78
78
|
const resolvedAgentId = readOptionalString(agentResolution?.agentId) ?? agentId
|
|
79
79
|
const config = getAgentRuntimeConfig({
|
|
80
80
|
agentId: resolvedAgentId,
|
|
81
|
-
|
|
82
|
-
mode: '
|
|
81
|
+
threadType: 'group' as const,
|
|
82
|
+
mode: 'fixedThreadMode',
|
|
83
83
|
onboardingActive: false,
|
|
84
84
|
linearInstalled: false,
|
|
85
85
|
systemWorkspaceDetails: runParams.systemWorkspaceDetails,
|
|
@@ -97,7 +97,7 @@ export function createTeamThinkTool(params: {
|
|
|
97
97
|
workspaceId: params.orgId,
|
|
98
98
|
userId: params.userId,
|
|
99
99
|
workspaceIdString: recordIdToString(params.orgId, TABLES.ORGANIZATION),
|
|
100
|
-
|
|
100
|
+
threadId: params.threadId,
|
|
101
101
|
githubInstalled: params.githubInstalled,
|
|
102
102
|
provideRepoTool: resolvedAgentId !== 'mentor' && params.provideRepoTool,
|
|
103
103
|
availableUploads: params.availableUploads,
|
|
@@ -110,7 +110,7 @@ export function createTeamThinkTool(params: {
|
|
|
110
110
|
const configuredMaxSteps = typeof agentConfig.maxSteps === 'number' ? agentConfig.maxSteps : 10
|
|
111
111
|
const maxSteps = Math.min(configuredMaxSteps, TEAM_THINK_AGENT_MAX_STEPS)
|
|
112
112
|
const agent = createAgent[agentId_]({
|
|
113
|
-
mode: '
|
|
113
|
+
mode: 'fixedThreadMode',
|
|
114
114
|
tools,
|
|
115
115
|
extraInstructions: agentConfig.extraInstructions,
|
|
116
116
|
maxRetries: TEAM_THINK_AGENT_MAX_RETRIES,
|
|
@@ -2,7 +2,7 @@ import { isAgentName } from '../config/agent-defaults'
|
|
|
2
2
|
import { compactWhitespace } from '../utils/string'
|
|
3
3
|
|
|
4
4
|
interface DigestMessageForTranscript {
|
|
5
|
-
source: '
|
|
5
|
+
source: 'thread' | 'social'
|
|
6
6
|
sourceId: string
|
|
7
7
|
role: 'system' | 'user' | 'assistant'
|
|
8
8
|
parts: Array<Record<string, unknown>>
|