@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.
Files changed (102) hide show
  1. package/infrastructure/schema/00_identity.surql +2 -2
  2. package/infrastructure/schema/00_thread.surql +75 -0
  3. package/infrastructure/schema/02_execution_plan.surql +10 -11
  4. package/infrastructure/schema/10_autonomous_job.surql +3 -3
  5. package/package.json +2 -2
  6. package/src/ai/definitions.ts +1 -1
  7. package/src/config/agent-defaults.ts +5 -5
  8. package/src/config/index.ts +1 -1
  9. package/src/config/thread-defaults.ts +72 -0
  10. package/src/create-runtime.ts +89 -93
  11. package/src/db/tables.ts +3 -3
  12. package/src/db/{workstream-message-row.ts → thread-message-row.ts} +3 -3
  13. package/src/queues/context-compaction.queue.ts +6 -6
  14. package/src/queues/plan-agent-heartbeat.queue.ts +3 -3
  15. package/src/queues/post-chat-memory.queue.ts +1 -1
  16. package/src/queues/title-generation.queue.ts +10 -13
  17. package/src/redis/index.ts +1 -1
  18. package/src/redis/stream-context.ts +1 -1
  19. package/src/runtime/agent-identity-overrides.ts +1 -1
  20. package/src/runtime/agent-runtime-policy.ts +19 -21
  21. package/src/runtime/chat-request-routing.ts +1 -1
  22. package/src/runtime/context-compaction-constants.ts +1 -1
  23. package/src/runtime/context-compaction.ts +1 -1
  24. package/src/runtime/execution-plan.ts +1 -1
  25. package/src/runtime/index.ts +1 -1
  26. package/src/runtime/memory-digest-policy.ts +1 -1
  27. package/src/runtime/plugin-types.ts +1 -1
  28. package/src/runtime/post-turn-side-effects.ts +35 -35
  29. package/src/runtime/runtime-config.ts +12 -12
  30. package/src/runtime/runtime-extensions.ts +11 -11
  31. package/src/runtime/social-chat-agent-runner.ts +3 -3
  32. package/src/runtime/social-chat-history.ts +1 -1
  33. package/src/runtime/social-chat.ts +6 -6
  34. package/src/runtime/team-consultation-orchestrator.ts +1 -1
  35. package/src/runtime/{workstream-chat-helpers.ts → thread-chat-helpers.ts} +7 -7
  36. package/src/runtime/{workstream-plan-turn.ts → thread-plan-turn.ts} +11 -17
  37. package/src/runtime/{workstream-turn-context.ts → thread-turn-context.ts} +10 -10
  38. package/src/services/agent-activity.service.ts +39 -44
  39. package/src/services/agent-executor.service.ts +17 -19
  40. package/src/services/attachment.service.ts +4 -8
  41. package/src/services/autonomous-job.service.ts +29 -28
  42. package/src/services/context-compaction.service.ts +19 -29
  43. package/src/services/execution-plan.service.ts +58 -70
  44. package/src/services/global-orchestrator.service.ts +5 -5
  45. package/src/services/index.ts +6 -6
  46. package/src/services/memory.service.ts +1 -1
  47. package/src/services/monitoring-window.service.ts +2 -2
  48. package/src/services/mutating-approval.service.ts +7 -10
  49. package/src/services/node-workspace.service.ts +8 -7
  50. package/src/services/notification.service.ts +1 -1
  51. package/src/services/organization.service.ts +9 -9
  52. package/src/services/ownership-dispatcher.service.ts +13 -19
  53. package/src/services/plan-agent-heartbeat.service.ts +13 -13
  54. package/src/services/plan-agent-query.service.ts +7 -7
  55. package/src/services/plan-artifact.service.ts +1 -2
  56. package/src/services/plan-coordination.service.ts +4 -4
  57. package/src/services/plan-cycle.service.ts +7 -7
  58. package/src/services/plan-deadline.service.ts +4 -4
  59. package/src/services/plan-event-delivery.service.ts +8 -12
  60. package/src/services/plan-executor.service.ts +16 -37
  61. package/src/services/plan-run-data.ts +27 -8
  62. package/src/services/plan-run.service.ts +7 -9
  63. package/src/services/plan-scheduler.service.ts +4 -4
  64. package/src/services/plan-template.service.ts +2 -2
  65. package/src/services/plan-validator.service.ts +0 -11
  66. package/src/services/plugin-executor.service.ts +1 -1
  67. package/src/services/queue-job.service.ts +1 -1
  68. package/src/services/recent-activity-title.service.ts +1 -1
  69. package/src/services/recent-activity.service.ts +4 -4
  70. package/src/services/system-executor.service.ts +2 -2
  71. package/src/services/{workstream-message.service.ts → thread-message.service.ts} +72 -76
  72. package/src/services/thread-plan-registry.service.ts +22 -0
  73. package/src/services/thread-title.service.ts +39 -0
  74. package/src/services/{workstream-turn-preparation.service.ts → thread-turn-preparation.service.ts} +131 -143
  75. package/src/services/{workstream-turn.ts → thread-turn.ts} +27 -31
  76. package/src/services/thread.service.ts +707 -0
  77. package/src/services/thread.types.ts +17 -0
  78. package/src/storage/attachment-storage.service.ts +4 -4
  79. package/src/system-agents/index.ts +1 -1
  80. package/src/system-agents/memory.agent.ts +1 -1
  81. package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
  82. package/src/system-agents/regular-chat-memory-digest.agent.ts +1 -1
  83. package/src/system-agents/researcher.agent.ts +3 -3
  84. package/src/system-agents/{workstream-router.agent.ts → thread-router.agent.ts} +21 -21
  85. package/src/system-agents/title-generator.agent.ts +8 -8
  86. package/src/tools/execution-plan.tool.ts +39 -40
  87. package/src/tools/memory-block.tool.ts +4 -4
  88. package/src/tools/research-topic.tool.ts +1 -0
  89. package/src/tools/search-web.tool.ts +1 -1
  90. package/src/tools/search.tool.ts +4 -4
  91. package/src/tools/team-think.tool.ts +9 -9
  92. package/src/workers/regular-chat-memory-digest.helpers.ts +1 -1
  93. package/src/workers/regular-chat-memory-digest.runner.ts +43 -43
  94. package/src/workers/skill-extraction.runner.ts +9 -13
  95. package/src/workers/utils/{workstream-message-query.ts → thread-message-query.ts} +21 -21
  96. package/infrastructure/schema/00_workstream.surql +0 -64
  97. package/src/config/workstream-defaults.ts +0 -72
  98. package/src/services/workstream-plan-registry.service.ts +0 -22
  99. package/src/services/workstream-title.service.ts +0 -42
  100. package/src/services/workstream.service.ts +0 -803
  101. package/src/services/workstream.types.ts +0 -17
  102. /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 UploadedWorkstreamAttachment = {
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<UploadedWorkstreamAttachment> {
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 uploadWorkstreamAttachment({
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<UploadedWorkstreamAttachment> {
118
+ }): Promise<UploadedThreadAttachment> {
119
119
  const filename = file.name || 'attachment'
120
120
  const mediaType = file.type || inferContentType(filename)
121
121
  const sizeBytes = file.size
@@ -10,4 +10,4 @@ export * from './researcher.agent'
10
10
  export * from './skill-extractor.agent'
11
11
  export * from './skill-manager.agent'
12
12
  export * from './title-generator.agent'
13
- export * from './workstream-router.agent'
13
+ export * from './thread-router.agent'
@@ -40,7 +40,7 @@ Extract organization-relevant facts from the conversation.
40
40
  - User personal preferences
41
41
  - Transient requests
42
42
  - General knowledge questions
43
- - Temporary discussion workstreams
43
+ - Temporary discussion threads
44
44
  </ignore>
45
45
 
46
46
  <format>
@@ -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 "Workstream update".
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 "Workstream update".
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 [workstream:...] prefixes as thread context only.
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 independent search queries.
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-5 most authoritative/relevant URLs.
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 repeat (max 2 additional rounds).
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(`[workstream-router] ${label} raw:`, preview)
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 workstream message router. Decide which team member should respond FIRST to the user message.
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: 'workstream-router',
214
+ id: 'thread-router',
215
215
  model: aiGatewayChatModel(modelId),
216
- headers: buildAiGatewayDirectCacheHeaders('workstream-router'),
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 triageWorkstreamMessage(params: {
224
- workstreamTitle: string
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
- `Workstream: "${params.workstreamTitle}"`,
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('[workstream-router] triage failed:', error instanceof Error ? error.message : 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('[workstream-router] triage ignored non-json output')
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('[workstream-router] triage parse failed:', JSON.stringify(parsed.error.issues))
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('[workstream-router] triage returned empty agentId — fallback to owner')
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('[workstream-router] triage returned unknown agent:', parsed.data.agentId)
284
+ console.log('[thread-router] triage returned unknown agent:', parsed.data.agentId)
285
285
  return null
286
286
  }
287
287
 
288
- console.log('[workstream-router] triage routed to:', parsed.data.agentId)
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
- workstreamTitle: string
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
- `Workstream: "${params.workstreamTitle}"`,
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('[workstream-router] check failed:', error instanceof Error ? error.message : 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('[workstream-router] check ignored non-json output')
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('[workstream-router] check parse failed:', JSON.stringify(parsed.error.issues))
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('[workstream-router] check: done, no more agents needed')
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('[workstream-router] check: invalid agentId:', parsed.data.agentId)
359
+ console.log('[thread-router] check: invalid agentId:', parsed.data.agentId)
360
360
  return { done: true }
361
361
  }
362
362
 
363
- console.log('[workstream-router] check: next agent:', parsed.data.agentId)
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 WORKSTREAM_TITLE_MAX_TOKENS = 512
12
+ const THREAD_TITLE_MAX_TOKENS = 512
13
13
 
14
- export const WORKSTREAM_TITLE_GENERATOR_PROMPT = `<agent-instructions>
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 workstream or intent
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 createWorkstreamTitleGeneratorAgent(options: CreateHelperToolLoopAgentOptions) {
33
+ export function createThreadTitleGeneratorAgent(options: CreateHelperToolLoopAgentOptions) {
34
34
  return new ToolLoopAgent({
35
- id: 'workstream-title-generator',
35
+ id: 'thread-title-generator',
36
36
  model: aiGatewayModel(OPENROUTER_FAST_REASONING_MODEL_ID),
37
- headers: buildAiGatewayDirectCacheHeaders('workstream-title-generator'),
37
+ headers: buildAiGatewayDirectCacheHeaders('thread-title-generator'),
38
38
  providerOptions: OPENROUTER_MINIMAL_REASONING_PROVIDER_OPTIONS,
39
39
  ...resolveHelperAgentOptions(options, {
40
- instructions: WORKSTREAM_TITLE_GENERATOR_PROMPT,
41
- maxOutputTokens: WORKSTREAM_TITLE_MAX_TOKENS,
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, CreateProjectWithPlanResultData } from '@lota-sdk/shared'
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 { workstreamService } from '../services/workstream.service'
15
+ import { threadService } from '../services/thread.service'
16
16
 
17
- type ExecutionPlanWorkstreamService = Pick<
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
- | 'getActivePlansForWorkstream'
26
+ | 'getActivePlansForThread'
30
27
  >
31
28
 
32
29
  function extractDraft(input: ExecutionPlanArgs) {
33
- const { action, projectTitle, targetWorkstreamId, runId, reason, ...draftInput } = input
34
- return { action, projectTitle, targetWorkstreamId, runId, reason, draft: expandAgentPlanDraft(draftInput) }
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
- workstreamId: RecordIdRef
37
+ threadId: RecordIdRef
41
38
  agentId: string
42
39
  executionPlanService?: ExecutionPlanExecutionPlanService
43
- workstreamService?: ExecutionPlanWorkstreamService
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.workstreamService ?? workstreamService
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 workstream, 3+ nodes), replace (swap active plan), resume (resume interrupted plan).',
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, targetWorkstreamId, runId, reason, draft } = extractDraft(input)
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
- workstreamId: targetWorkstreamId ?? params.workstreamId,
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 targetWorkstream = targetWorkstreamId
70
- ? await resolvedWsService.getWorkstream(targetWorkstreamId)
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.createWorkstream(params.userId, params.orgId, {
73
+ return resolvedWsService.createThread({
74
+ userId: params.userId,
75
+ organizationId: params.orgId,
77
76
  title: projectTitle,
78
- mode: 'group',
77
+ type: 'group',
79
78
  })
80
79
  })()
81
80
 
82
- if (targetWorkstream.organizationId !== recordIdToString(params.orgId, TABLES.ORGANIZATION)) {
83
- throw new Error('Target workstream belongs to a different organization.')
81
+ if (targetThread.organizationId !== recordIdToString(params.orgId, TABLES.ORGANIZATION)) {
82
+ throw new Error('Target thread belongs to a different organization.')
84
83
  }
85
- if (targetWorkstream.userId !== recordIdToString(params.userId, TABLES.USER)) {
86
- throw new Error('Target workstream belongs to a different user.')
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.getActivePlansForWorkstream(targetWorkstream.id)
90
- if (!targetWorkstream.core && existingPlans.length > 0) {
88
+ const existingPlans = await resolvedEpService.getActivePlansForThread(targetThread.id)
89
+ if (targetThread.type !== 'thread' && existingPlans.length > 0) {
91
90
  throw new Error(
92
- 'This workstream already has an active execution plan. Use action "replace" or target a core workstream.',
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 createdWorkstream = !targetWorkstreamId
95
+ const createdThread = !targetThreadId
97
96
  try {
98
97
  const result = await resolvedEpService.createPlan({
99
98
  organizationId: params.orgId,
100
- workstreamId: targetWorkstream.id,
99
+ threadId: targetThread.id,
101
100
  leadAgentId: params.agentId,
102
101
  input: draft,
103
102
  })
104
103
  return {
105
104
  ...result,
106
- workstreamId: targetWorkstream.id,
107
- workstreamTitle: targetWorkstream.title,
108
- createdWorkstream,
105
+ threadId: targetThread.id,
106
+ threadTitle: targetThread.title,
107
+ createdThread,
109
108
  } satisfies CreateProjectWithPlanResultData
110
109
  } catch (error) {
111
- if (createdWorkstream) {
112
- await resolvedWsService.deleteWorkstream(targetWorkstream.id).catch(() => {})
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
- workstreamId: params.workstreamId,
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
- workstreamId: params.workstreamId,
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: { workstreamId: RecordIdRef }) {
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.workstreamId)
157
+ return await executionPlanService.listActivePlanSummaries(params.threadId)
159
158
  }
160
159
  return await executionPlanService.getActivePlanToolResult({
161
- workstreamId: params.workstreamId,
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
- workstreamId: RecordIdRef
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
- workstreamId: params.workstreamId,
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 { workstreamService } from '../services/workstream.service'
11
+ import { threadService } from '../services/thread.service'
12
12
  import { safeEnqueue } from '../utils/async'
13
13
 
14
14
  export function createMemoryBlockTool({
15
- workstreamId,
15
+ threadId,
16
16
  agentLabel,
17
17
  getCurrentBlock,
18
18
  onAppend,
19
19
  }: {
20
- workstreamId: RecordIdRef
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 workstreamService.appendMemoryBlock(workstreamId, prepared.formatted)
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).optional(),
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(),
@@ -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 { workstreamMessageService } from '../services/workstream-message.service'
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(workstreamId: RecordIdRef) {
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 workstreamMessageService.searchMessages({
46
- workstreamId,
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/workstream-chat-helpers'
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
- workstreamId: RecordIdRef
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: 'fixedWorkstreamMode',
64
- workstream: null,
65
- workstreamRef: params.workstreamId,
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
- workstreamMode: 'group' as const,
82
- mode: 'fixedWorkstreamMode',
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
- workstreamId: params.workstreamId,
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: 'fixedWorkstreamMode',
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: 'workstream' | 'social'
5
+ source: 'thread' | 'social'
6
6
  sourceId: string
7
7
  role: 'system' | 'user' | 'assistant'
8
8
  parts: Array<Record<string, unknown>>