@lota-sdk/core 0.2.3 → 0.3.1

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 (106) hide show
  1. package/infrastructure/schema/00_identity.surql +2 -2
  2. package/infrastructure/schema/00_thread.surql +73 -0
  3. package/infrastructure/schema/02_execution_plan.surql +10 -11
  4. package/infrastructure/schema/04_runtime_bootstrap.surql +1 -0
  5. package/infrastructure/schema/10_autonomous_job.surql +3 -3
  6. package/package.json +2 -2
  7. package/src/ai/definitions.ts +1 -1
  8. package/src/config/agent-defaults.ts +5 -5
  9. package/src/config/index.ts +1 -1
  10. package/src/config/thread-defaults.ts +72 -0
  11. package/src/create-runtime.ts +90 -94
  12. package/src/db/record-id.ts +21 -21
  13. package/src/db/service.ts +44 -40
  14. package/src/db/tables.ts +3 -3
  15. package/src/db/{workstream-message-row.ts → thread-message-row.ts} +3 -3
  16. package/src/queues/context-compaction.queue.ts +6 -6
  17. package/src/queues/plan-agent-heartbeat.queue.ts +3 -3
  18. package/src/queues/post-chat-memory.queue.ts +1 -1
  19. package/src/queues/title-generation.queue.ts +10 -13
  20. package/src/redis/index.ts +1 -1
  21. package/src/redis/stream-context.ts +1 -1
  22. package/src/runtime/agent-identity-overrides.ts +1 -1
  23. package/src/runtime/agent-runtime-policy.ts +19 -21
  24. package/src/runtime/chat-request-routing.ts +1 -1
  25. package/src/runtime/context-compaction-constants.ts +1 -1
  26. package/src/runtime/context-compaction.ts +1 -1
  27. package/src/runtime/execution-plan.ts +1 -1
  28. package/src/runtime/index.ts +1 -1
  29. package/src/runtime/memory-digest-policy.ts +1 -1
  30. package/src/runtime/plugin-types.ts +1 -1
  31. package/src/runtime/post-turn-side-effects.ts +35 -35
  32. package/src/runtime/runtime-config.ts +24 -21
  33. package/src/runtime/runtime-extensions.ts +11 -11
  34. package/src/runtime/social-chat-agent-runner.ts +3 -3
  35. package/src/runtime/social-chat-history.ts +1 -1
  36. package/src/runtime/social-chat.ts +6 -6
  37. package/src/runtime/team-consultation-orchestrator.ts +1 -1
  38. package/src/runtime/{workstream-chat-helpers.ts → thread-chat-helpers.ts} +7 -7
  39. package/src/runtime/{workstream-plan-turn.ts → thread-plan-turn.ts} +11 -17
  40. package/src/runtime/{workstream-turn-context.ts → thread-turn-context.ts} +10 -10
  41. package/src/services/agent-activity.service.ts +39 -44
  42. package/src/services/agent-executor.service.ts +17 -19
  43. package/src/services/attachment.service.ts +4 -8
  44. package/src/services/autonomous-job.service.ts +29 -28
  45. package/src/services/context-compaction.service.ts +19 -29
  46. package/src/services/execution-plan.service.ts +58 -70
  47. package/src/services/global-orchestrator.service.ts +5 -5
  48. package/src/services/index.ts +6 -6
  49. package/src/services/memory.service.ts +1 -1
  50. package/src/services/monitoring-window.service.ts +2 -2
  51. package/src/services/mutating-approval.service.ts +7 -10
  52. package/src/services/node-workspace.service.ts +8 -7
  53. package/src/services/notification.service.ts +1 -1
  54. package/src/services/organization.service.ts +9 -9
  55. package/src/services/ownership-dispatcher.service.ts +13 -19
  56. package/src/services/plan-agent-heartbeat.service.ts +13 -13
  57. package/src/services/plan-agent-query.service.ts +7 -7
  58. package/src/services/plan-artifact.service.ts +1 -2
  59. package/src/services/plan-coordination.service.ts +4 -4
  60. package/src/services/plan-cycle.service.ts +7 -7
  61. package/src/services/plan-deadline.service.ts +4 -4
  62. package/src/services/plan-event-delivery.service.ts +8 -12
  63. package/src/services/plan-executor.service.ts +25 -39
  64. package/src/services/plan-run-data.ts +27 -8
  65. package/src/services/plan-run.service.ts +7 -9
  66. package/src/services/plan-scheduler.service.ts +4 -4
  67. package/src/services/plan-template.service.ts +2 -2
  68. package/src/services/plan-validator.service.ts +0 -11
  69. package/src/services/plugin-executor.service.ts +1 -1
  70. package/src/services/queue-job.service.ts +1 -1
  71. package/src/services/recent-activity-title.service.ts +1 -1
  72. package/src/services/recent-activity.service.ts +4 -4
  73. package/src/services/system-executor.service.ts +2 -2
  74. package/src/services/{workstream-message.service.ts → thread-message.service.ts} +72 -76
  75. package/src/services/thread-plan-registry.service.ts +22 -0
  76. package/src/services/thread-title.service.ts +39 -0
  77. package/src/services/{workstream-turn-preparation.service.ts → thread-turn-preparation.service.ts} +148 -171
  78. package/src/services/{workstream-turn.ts → thread-turn.ts} +27 -31
  79. package/src/services/thread.service.ts +853 -0
  80. package/src/services/thread.types.ts +17 -0
  81. package/src/storage/attachment-storage.service.ts +4 -4
  82. package/src/system-agents/index.ts +1 -1
  83. package/src/system-agents/memory.agent.ts +1 -1
  84. package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
  85. package/src/system-agents/regular-chat-memory-digest.agent.ts +1 -1
  86. package/src/system-agents/researcher.agent.ts +3 -3
  87. package/src/system-agents/{workstream-router.agent.ts → thread-router.agent.ts} +68 -135
  88. package/src/system-agents/title-generator.agent.ts +8 -8
  89. package/src/tools/execution-plan.tool.ts +39 -40
  90. package/src/tools/memory-block.tool.ts +4 -4
  91. package/src/tools/research-topic.tool.ts +1 -0
  92. package/src/tools/search-web.tool.ts +1 -1
  93. package/src/tools/search.tool.ts +4 -4
  94. package/src/tools/team-think.tool.ts +9 -9
  95. package/src/utils/async.ts +6 -7
  96. package/src/workers/regular-chat-memory-digest.helpers.ts +1 -1
  97. package/src/workers/regular-chat-memory-digest.runner.ts +43 -43
  98. package/src/workers/skill-extraction.runner.ts +9 -13
  99. package/src/workers/utils/{workstream-message-query.ts → thread-message-query.ts} +21 -21
  100. package/infrastructure/schema/00_workstream.surql +0 -64
  101. package/src/config/workstream-defaults.ts +0 -72
  102. package/src/services/workstream-plan-registry.service.ts +0 -22
  103. package/src/services/workstream-title.service.ts +0 -42
  104. package/src/services/workstream.service.ts +0 -803
  105. package/src/services/workstream.types.ts +0 -17
  106. /package/src/services/{workstream-constants.ts → thread-constants.ts} +0 -0
@@ -3,18 +3,18 @@ import type { Job } from 'bullmq'
3
3
  import { ensureRecordId } from '../db/record-id'
4
4
  import { databaseService } from '../db/service'
5
5
  import { recentActivityTitleService } from '../services/recent-activity-title.service'
6
- import { workstreamTitleService } from '../services/workstream-title.service'
6
+ import { threadTitleService } from '../services/thread-title.service'
7
7
  import { createQueueFactory } from './queue-factory'
8
8
 
9
9
  export const TITLE_GENERATION_QUEUE = 'title-generation'
10
10
 
11
- // This queue merges workstream title generation and recent-activity title
11
+ // This queue merges thread title generation and recent-activity title
12
12
  // refinement because both are short-lived title synthesis jobs with the same
13
13
  // operational shape.
14
14
 
15
- interface WorkstreamTitleGenerationJob {
16
- kind: 'workstream-title'
17
- workstreamId: string
15
+ interface ThreadTitleGenerationJob {
16
+ kind: 'thread-title'
17
+ threadId: string
18
18
  sourceText: string
19
19
  }
20
20
 
@@ -23,12 +23,12 @@ interface RecentActivityTitleRefinementJob {
23
23
  activityId: string
24
24
  }
25
25
 
26
- type TitleGenerationJob = WorkstreamTitleGenerationJob | RecentActivityTitleRefinementJob
26
+ type TitleGenerationJob = ThreadTitleGenerationJob | RecentActivityTitleRefinementJob
27
27
 
28
28
  async function processTitleGenerationJob(job: Job<TitleGenerationJob>): Promise<void> {
29
29
  await databaseService.connect()
30
- if (job.data.kind === 'workstream-title') {
31
- await workstreamTitleService.generateAndPersistTitle(ensureRecordId(job.data.workstreamId), job.data.sourceText)
30
+ if (job.data.kind === 'thread-title') {
31
+ await threadTitleService.generateAndPersistTitle(ensureRecordId(job.data.threadId), job.data.sourceText)
32
32
  return
33
33
  }
34
34
 
@@ -45,11 +45,8 @@ const titleGeneration = createQueueFactory<TitleGenerationJob>({
45
45
  processor: processTitleGenerationJob,
46
46
  })
47
47
 
48
- export function enqueueWorkstreamTitleGeneration(job: Omit<WorkstreamTitleGenerationJob, 'kind'>) {
49
- return titleGeneration.enqueue(
50
- { kind: 'workstream-title', ...job },
51
- { jobId: `workstream-title:${job.workstreamId}` },
52
- )
48
+ export function enqueueThreadTitleGeneration(job: Omit<ThreadTitleGenerationJob, 'kind'>) {
49
+ return titleGeneration.enqueue({ kind: 'thread-title', ...job }, { jobId: `thread-title:${job.threadId}` })
53
50
  }
54
51
 
55
52
  export function enqueueRecentActivityTitleRefinement(job: Omit<RecentActivityTitleRefinementJob, 'kind'>) {
@@ -9,7 +9,7 @@ export {
9
9
  } from './connection-accessor'
10
10
  export { withOrgMemoryLock } from './org-memory-lock'
11
11
  export { LeaseLockLostError, withRedisLeaseLock } from './redis-lease-lock'
12
- export { closeSharedSubscriber, createWorkstreamResumableContext } from './stream-context'
12
+ export { closeSharedSubscriber, createThreadResumableContext } from './stream-context'
13
13
 
14
14
  export { createRedisConnectionManager }
15
15
  export type { RedisConnectionManager }
@@ -55,7 +55,7 @@ export async function closeSharedSubscriber(): Promise<void> {
55
55
  }
56
56
  }
57
57
 
58
- export function createWorkstreamResumableContext() {
58
+ export function createThreadResumableContext() {
59
59
  const redis = getRedisConnection()
60
60
  return createResumableStreamContext({
61
61
  waitUntil: null,
@@ -1,5 +1,5 @@
1
1
  import { agentDisplayNames } from '../config/agent-defaults'
2
- import { asRecord, readOptionalString } from './workstream-chat-helpers'
2
+ import { asRecord, readOptionalString } from './thread-chat-helpers'
3
3
 
4
4
  interface RuntimeAgentIdentityOverrides {
5
5
  displayNamesById: Partial<Record<string, string>>
@@ -7,7 +7,7 @@ import type {
7
7
  } from '@lota-sdk/shared'
8
8
 
9
9
  import { getLeadAgentId } from '../config/agent-defaults'
10
- import { resolveOnboardingOwnerAgentId } from '../config/workstream-defaults'
10
+ import { resolveOnboardingOwnerAgentId } from '../config/thread-defaults'
11
11
  import type { ChatMode } from './agent-types'
12
12
  export interface AgentRuntimeConfig<TAgent extends string> {
13
13
  id: TAgent
@@ -73,15 +73,13 @@ function buildOwnershipDispatchArtifactPayload(artifacts: PlanArtifactSubmission
73
73
  return artifacts.map((artifact) => ({
74
74
  name: artifact.name,
75
75
  kind: artifact.kind,
76
- pointer: artifact.pointer,
77
- ...(artifact.schemaRef ? { schemaRef: artifact.schemaRef } : {}),
78
76
  ...(artifact.description ? { description: artifact.description } : {}),
79
77
  ...(artifact.payload !== undefined ? { payload: artifact.payload } : {}),
80
78
  }))
81
79
  }
82
80
 
83
- export function toChatMode(workstreamMode: 'direct' | 'group'): ChatMode {
84
- return workstreamMode === 'direct' ? 'fixedWorkstreamMode' : 'workstreamMode'
81
+ export function toChatMode(threadType: string): ChatMode {
82
+ return threadType === 'default' ? 'fixedThreadMode' : 'threadMode'
85
83
  }
86
84
 
87
85
  function toMemoryBlockSection(memoryBlock: string | undefined): string | undefined {
@@ -92,13 +90,13 @@ function toMemoryBlockSection(memoryBlock: string | undefined): string | undefin
92
90
 
93
91
  export function resolveActiveAgentSkills<TAgent extends string, TSkill extends PropertyKey>(params: {
94
92
  agentId: TAgent
95
- workstreamMode: 'direct' | 'group'
93
+ threadType: string
96
94
  mode?: ChatMode
97
95
  onboardingActive: boolean
98
96
  linearInstalled: boolean
99
97
  getAgentSkills: (agentId: TAgent, mode: ChatMode) => TSkill[]
100
98
  }): TSkill[] {
101
- const mode = params.mode ?? toChatMode(params.workstreamMode)
99
+ const mode = params.mode ?? toChatMode(params.threadType)
102
100
  return params
103
101
  .getAgentSkills(params.agentId, mode)
104
102
  .filter((skill) => (params.linearInstalled ? true : skill !== ('linear' as TSkill)))
@@ -107,7 +105,7 @@ export function resolveActiveAgentSkills<TAgent extends string, TSkill extends P
107
105
  export function buildAgentRuntimeConfig<TAgent extends string, TSkill extends PropertyKey>(params: {
108
106
  agentId: TAgent
109
107
  displayNameByAgent: Record<TAgent, string>
110
- workstreamMode: 'direct' | 'group'
108
+ threadType: string
111
109
  mode?: ChatMode
112
110
  skills?: TSkill[]
113
111
  onboardingActive: boolean
@@ -115,7 +113,7 @@ export function buildAgentRuntimeConfig<TAgent extends string, TSkill extends Pr
115
113
  systemWorkspaceDetails?: string
116
114
  preSeededMemoriesSection?: string
117
115
  retrievedKnowledgeSection?: string
118
- workstreamMemoryBlock?: string
116
+ threadMemoryBlock?: string
119
117
  responseGuardSection?: string
120
118
  learnedSkillsSection?: string
121
119
  additionalInstructionSections?: string[]
@@ -125,7 +123,7 @@ export function buildAgentRuntimeConfig<TAgent extends string, TSkill extends Pr
125
123
  buildSkillInstructionSection: (skills: TSkill[]) => string
126
124
  buildOnboardingPromptSection: () => string
127
125
  }): AgentRuntimeConfig<TAgent> {
128
- const mode = params.mode ?? toChatMode(params.workstreamMode)
126
+ const mode = params.mode ?? toChatMode(params.threadType)
129
127
  const rulesSection = params.buildGlobalRuleInstructionSection(params.ruleOptions)
130
128
  const skillsSection =
131
129
  params.skills && params.skills.length > 0 ? params.buildSkillInstructionSection(params.skills) : ''
@@ -138,7 +136,7 @@ export function buildAgentRuntimeConfig<TAgent extends string, TSkill extends Pr
138
136
  params.systemWorkspaceDetails?.trim(),
139
137
  params.preSeededMemoriesSection?.trim(),
140
138
  params.retrievedKnowledgeSection?.trim(),
141
- toMemoryBlockSection(params.workstreamMemoryBlock),
139
+ toMemoryBlockSection(params.threadMemoryBlock),
142
140
  ...(params.additionalInstructionSections?.map((section) => section.trim()) ?? []),
143
141
  params.responseGuardSection?.trim(),
144
142
  params.onboardingActive ? 'Onboarding is active. Keep responses onboarding-focused and concise.' : undefined,
@@ -154,9 +152,9 @@ export function buildAgentRuntimeConfig<TAgent extends string, TSkill extends Pr
154
152
  }
155
153
  }
156
154
 
157
- export function buildWorkstreamAgentToolPolicy<TAgent extends string, TSkill extends PropertyKey>(params: {
155
+ export function buildThreadAgentToolPolicy<TAgent extends string, TSkill extends PropertyKey>(params: {
158
156
  agentId: TAgent
159
- workstreamMode: 'direct' | 'group'
157
+ threadType: string
160
158
  mode?: ChatMode
161
159
  onboardingActive: boolean
162
160
  linearInstalled: boolean
@@ -164,11 +162,11 @@ export function buildWorkstreamAgentToolPolicy<TAgent extends string, TSkill ext
164
162
  provideRepoTool: boolean
165
163
  getAgentSkills: (agentId: TAgent, mode: ChatMode) => TSkill[]
166
164
  }): AgentToolPolicy<TSkill> {
167
- const resolvedMode = params.mode ?? toChatMode(params.workstreamMode)
165
+ const resolvedMode = params.mode ?? toChatMode(params.threadType)
168
166
  const onboardingOwnerAgentId = resolveOnboardingOwnerAgentId(getLeadAgentId()) as TAgent
169
167
  const skills = resolveActiveAgentSkills({
170
168
  agentId: params.agentId,
171
- workstreamMode: params.workstreamMode,
169
+ threadType: params.threadType,
172
170
  mode: resolvedMode,
173
171
  onboardingActive: params.onboardingActive,
174
172
  linearInstalled: params.linearInstalled,
@@ -203,7 +201,7 @@ export function buildTeamConsultationAgentToolPolicy({
203
201
  provideRepoTool: boolean
204
202
  }): AgentToolPolicy<string> & { blockedToolNames: Set<string> } {
205
203
  return {
206
- resolvedMode: 'fixedWorkstreamMode',
204
+ resolvedMode: 'fixedThreadMode',
207
205
  skills: [],
208
206
  includeMemorySearch: false,
209
207
  includeConversationSearch: false,
@@ -249,9 +247,9 @@ export function buildOwnershipDispatchContextSection(params: {
249
247
  return [
250
248
  '<ownership-dispatch-execution>',
251
249
  'You are executing a single isolated execution-plan node.',
252
- 'Do not ask the user questions. Do not reference any hidden or prior workstream chat history.',
253
- 'Use only the provided node context, resolved input, input artifacts, and upstream handoff context.',
254
- 'Return only the final structured node result that satisfies the required output contract, including durable handoffContext for downstream nodes.',
250
+ 'Do not ask the user questions. Do not reference any hidden or prior thread chat history.',
251
+ 'Use only the provided node context, resolved input, input artifacts, and upstream summaries.',
252
+ 'Return only the final structured node result that satisfies the required output contract.',
255
253
  JSON.stringify(payload, null, 2),
256
254
  '</ownership-dispatch-execution>',
257
255
  ].join('\n')
@@ -268,14 +266,14 @@ export function buildOwnershipDispatchResponseGuard(params: {
268
266
  return [
269
267
  '<ownership-dispatch-result-contract>',
270
268
  'Return a single JSON object with this exact shape:',
271
- '{"structuredOutput"?: object, "artifacts": Array<{ "name": string, "kind": "json"|"markdown"|"file"|"external-ref"|"record", "pointer": string, "schemaRef"?: string, "description"?: string, "payload"?: object|array }>, "notes"?: string, "handoffContext"?: { "summary": string, "keyDecisions"?: string[], "openQuestions"?: string[], "risks"?: string[], "recommendations"?: string[], "references"?: string[] }}',
269
+ '{"notes": string, "structuredOutput"?: object, "artifacts": Array<{ "name": string, "kind": "json"|"markdown"|"file"|"external-ref"|"record", "description"?: string, "payload"?: object|array }>}',
272
270
  'Do not wrap the JSON in markdown or code fences.',
273
271
  `Node label: ${params.node.label}`,
274
272
  `Required deliverables: ${params.node.deliverables.length > 0 ? params.node.deliverables.map((item) => item.name).join(', ') : 'none'}`,
275
273
  ...(completionCheckHints.length > 0
276
274
  ? ['Structured output fields required by completion checks:', ...completionCheckHints]
277
275
  : []),
278
- 'If downstream nodes depend on this work, include handoffContext.',
276
+ 'notes is required. Include a concise summary of what was done, key decisions, and any context downstream nodes need.',
279
277
  '</ownership-dispatch-result-contract>',
280
278
  ].join('\n')
281
279
  }
@@ -8,7 +8,7 @@ export type RoutedChatRequest =
8
8
  | { kind: 'turn'; inputMessage: ChatMessage }
9
9
  | { kind: 'invalid'; message: string }
10
10
 
11
- export function routeWorkstreamChatMessages(messages: ChatMessage[]): RoutedChatRequest {
11
+ export function routeThreadChatMessages(messages: ChatMessage[]): RoutedChatRequest {
12
12
  if (isApprovalContinuationRequest(messages)) {
13
13
  if (isNativeToolApprovalRequest(messages)) {
14
14
  return { kind: 'native-tool-approval', messages }
@@ -10,4 +10,4 @@ export const SUMMARY_ROLLUP_MAX_TOKENS = 80_000
10
10
  export const CONTEXT_COMPACTION_INCLUDED_TOOL_NAMES = ['userQuestions', 'proceedInOnboarding'] as const
11
11
  export const CONTEXT_COMPACTION_INCLUDED_TOOL_PREFIXES = ['linear'] as const
12
12
  export const CONTEXT_WINDOW_TOKENS = 200_000
13
- export const WORKSTREAM_RAW_TAIL_MESSAGES = 6
13
+ export const THREAD_RAW_TAIL_MESSAGES = 6
@@ -198,7 +198,7 @@ export function buildContextCompactionPrompt(params: ContextCompactionPromptPara
198
198
  export function buildMemoryBlockCompactionPrompt(params: MemoryBlockCompactionPromptParams): string {
199
199
  return [
200
200
  '<memory-block-compaction>',
201
- 'Produce a compact replacement summary for the workstream memory block.',
201
+ 'Produce a compact replacement summary for the thread memory block.',
202
202
  'Preserve constraints, commitments, unresolved risks, and ownership.',
203
203
  'Blend the previous summary with the new raw entries into one updated summary.',
204
204
  'Return plain text only.',
@@ -7,7 +7,7 @@ const EXECUTION_PLAN_AGENT_PROTOCOL_PROMPT = `<execution-plan-protocol>
7
7
  - The active execution runs in <execution-plan-state> are a summary inventory only. They list runId and title, not node-level state.
8
8
  - The runtime executor owns lifecycle truth. Do not claim node completion until the executor confirms.
9
9
  - Do not invent or restate run/node lifecycle details that are not present in the prompt or tool results.
10
- - During plan-triggered turns, use the dedicated result-submission tool. Include handoffContext.
10
+ - During plan-triggered turns, use the dedicated result-submission tool.
11
11
  - Treat the active execution runs in <execution-plan-state> as authoritative for whether a plan already exists.
12
12
  - If contracts or criteria materially change, replace the plan.
13
13
  </execution-plan-protocol>`
@@ -24,4 +24,4 @@ export * from './social-chat'
24
24
  export * from './team-consultation-orchestrator'
25
25
  export * from './team-consultation-prompts'
26
26
  export * from './turn-lifecycle'
27
- export * from './workstream-chat-helpers'
27
+ export * from './thread-chat-helpers'
@@ -11,7 +11,7 @@ export function shouldEnqueueOnboardingPostChatMemory(params: {
11
11
  return params.userMessageText.trim().length > 0 || params.hasAttachmentContext
12
12
  }
13
13
 
14
- export function shouldEnqueueRegularDigestForWorkstream(params: {
14
+ export function shouldEnqueueRegularDigestForThread(params: {
15
15
  onboardingActive: boolean
16
16
  turnCount?: number
17
17
  }): boolean {
@@ -9,7 +9,7 @@ export interface LotaPluginContributions {
9
9
 
10
10
  export interface PluginNodeExecutorContext {
11
11
  organizationId: string
12
- workstreamId: string
12
+ threadId: string
13
13
  planId: string
14
14
  nodeId: string
15
15
  userId?: RecordIdRef
@@ -10,22 +10,22 @@ import {
10
10
  shouldEnqueueMemoryConsolidation,
11
11
  shouldEnqueueMemoryExtraction,
12
12
  shouldEnqueueOnboardingPostChatMemory,
13
- shouldEnqueueRegularDigestForWorkstream,
13
+ shouldEnqueueRegularDigestForThread,
14
14
  } from '../runtime/memory-digest-policy'
15
15
  import { getRuntimeAdapters } from '../runtime/runtime-extensions'
16
16
  import { shouldEnqueueSkillExtraction } from '../runtime/skill-extraction-policy'
17
17
  import {
18
- appendPersistedWorkstreamContextToHistoryMessages,
18
+ appendPersistedThreadContextToHistoryMessages,
19
19
  buildAgentHistoryMessages,
20
20
  buildConversationSummary,
21
21
  buildReadableUploadMetadataContext,
22
22
  extractMessageText,
23
23
  readOptionalString,
24
24
  toHistoryMessages,
25
- } from '../runtime/workstream-chat-helpers'
25
+ } from '../runtime/thread-chat-helpers'
26
26
  import { recentActivityService } from '../services/recent-activity.service'
27
- import { workstreamService } from '../services/workstream.service'
28
- import type { NormalizedWorkstream, WorkstreamRecord } from '../services/workstream.types'
27
+ import { threadService } from '../services/thread.service'
28
+ import type { NormalizedThread, ThreadRecord } from '../services/thread.types'
29
29
  import { safeEnqueue } from '../utils/async'
30
30
  import { toIsoDateTimeString } from '../utils/date-time'
31
31
 
@@ -41,37 +41,37 @@ function resolveDisplayName(agentId: string, overrides?: Partial<Record<string,
41
41
  }
42
42
 
43
43
  function buildRecentActivityChatDeepLink(params: {
44
- workstream: NormalizedWorkstream
45
- workstreamId: string
44
+ thread: NormalizedThread
45
+ threadId: string
46
46
  visibleAgentId: string
47
47
  }): { route: string; search: Record<string, string> } {
48
- if (params.workstream.mode === 'direct') {
49
- return { route: 'direct-workstream', search: { workstreamId: params.workstreamId, agentId: params.visibleAgentId } }
48
+ if (params.thread.type === 'default') {
49
+ return { route: 'direct-thread', search: { threadId: params.threadId, agentId: params.visibleAgentId } }
50
50
  }
51
51
 
52
- return { route: 'group-workstream', search: { workstreamId: params.workstreamId } }
52
+ return { route: 'group-thread', search: { threadId: params.threadId } }
53
53
  }
54
54
 
55
55
  function buildRecentActivityChatSystemTitle(params: {
56
- workstream: NormalizedWorkstream
56
+ thread: NormalizedThread
57
57
  visibleAgentId: string
58
58
  agentDisplayNamesById?: Partial<Record<string, string>>
59
59
  }): string {
60
- if (params.workstream.mode === 'direct') {
60
+ if (params.thread.type === 'default') {
61
61
  return `Conversation with ${resolveDisplayName(params.visibleAgentId, params.agentDisplayNamesById)}`
62
62
  }
63
63
 
64
- return params.workstream.title.trim() || 'Workstream update'
64
+ return params.thread.title.trim() || 'Thread update'
65
65
  }
66
66
 
67
67
  interface PostTurnSideEffectsParams {
68
- workstream: NormalizedWorkstream
69
- workstreamRef: RecordIdRef
68
+ thread: NormalizedThread
69
+ threadRef: RecordIdRef
70
70
  orgRef: RecordIdRef
71
71
  userRef: RecordIdRef
72
72
  userName?: string | null
73
73
  orgIdString: string
74
- workstreamIdString: string
74
+ threadIdString: string
75
75
  onboardingActive: boolean
76
76
  workspace: unknown
77
77
  allAssistantMessages: ChatMessage[]
@@ -85,19 +85,19 @@ interface PostTurnSideEffectsParams {
85
85
  storageKey: string
86
86
  }>
87
87
  memoryBlock: string
88
- visibleWorkstreamAgentId: string | null | undefined
88
+ visibleThreadAgentId: string | null | undefined
89
89
  defaultLeadAgentId: string
90
- latestWorkstreamRecord: WorkstreamRecord
90
+ latestThreadRecord: ThreadRecord
91
91
  isUserTurn: boolean
92
92
  agentDisplayNamesById?: Partial<Record<string, string>>
93
93
  }
94
94
 
95
95
  export async function runPostTurnSideEffects(params: PostTurnSideEffectsParams): Promise<void> {
96
96
  const recentHistory = await params.loadRecentHistory()
97
- const turnCount = await workstreamService.incrementTurnCount(params.workstreamRef)
97
+ const turnCount = await threadService.incrementTurnCount(params.threadRef)
98
98
  const agentMessages = buildAgentHistoryMessages(params.allAssistantMessages)
99
- const historyMessagesForMemory = appendPersistedWorkstreamContextToHistoryMessages(toHistoryMessages(recentHistory), {
100
- compactionSummary: params.latestWorkstreamRecord.compactionSummary,
99
+ const historyMessagesForMemory = appendPersistedThreadContextToHistoryMessages(toHistoryMessages(recentHistory), {
100
+ compactionSummary: params.latestThreadRecord.compactionSummary,
101
101
  })
102
102
 
103
103
  const userMessageText = params.referenceUserMessage ? extractMessageText(params.referenceUserMessage).trim() : ''
@@ -120,7 +120,7 @@ export async function runPostTurnSideEffects(params: PostTurnSideEffectsParams):
120
120
  () =>
121
121
  enqueuePostChatMemory({
122
122
  orgId: params.orgIdString,
123
- workstreamId: params.workstreamIdString,
123
+ threadId: params.threadIdString,
124
124
  sourceId: params.referenceUserMessageId,
125
125
  onboardStatus: readOptionalString((params.workspace as { onboardStatus?: unknown }).onboardStatus),
126
126
  userMessage: memoryUserMessage,
@@ -139,7 +139,7 @@ export async function runPostTurnSideEffects(params: PostTurnSideEffectsParams):
139
139
  assistantMessages: params.allAssistantMessages,
140
140
  })
141
141
  if (conversationSummary) {
142
- const effectiveAgentId = params.visibleWorkstreamAgentId ?? params.defaultLeadAgentId
142
+ const effectiveAgentId = params.visibleThreadAgentId ?? params.defaultLeadAgentId
143
143
  const recentActivityResult = await recentActivityService.recordEvent({
144
144
  orgId: params.orgRef,
145
145
  userId: params.userRef,
@@ -147,27 +147,27 @@ export async function runPostTurnSideEffects(params: PostTurnSideEffectsParams):
147
147
  event: {
148
148
  sourceEventId: `chat-turn:${params.referenceUserMessageId}`,
149
149
  kind: 'chat.turn.completed',
150
- targetKind: 'workstream',
151
- targetId: params.workstreamIdString,
152
- mergeKey: `workstream:${params.workstreamIdString}`,
150
+ targetKind: 'thread',
151
+ targetId: params.threadIdString,
152
+ mergeKey: `thread:${params.threadIdString}`,
153
153
  title: buildRecentActivityChatSystemTitle({
154
- workstream: params.workstream,
154
+ thread: params.thread,
155
155
  visibleAgentId: effectiveAgentId,
156
156
  agentDisplayNamesById: params.agentDisplayNamesById,
157
157
  }),
158
158
  sourceLabel: resolveDisplayName(effectiveAgentId, params.agentDisplayNamesById),
159
159
  deepLink: buildRecentActivityChatDeepLink({
160
- workstream: params.workstream,
161
- workstreamId: params.workstreamIdString,
160
+ thread: params.thread,
161
+ threadId: params.threadIdString,
162
162
  visibleAgentId: effectiveAgentId,
163
163
  }),
164
164
  metadata: {
165
165
  agentId: effectiveAgentId,
166
166
  agentName: resolveDisplayName(effectiveAgentId, params.agentDisplayNamesById),
167
- workstreamId: params.workstreamIdString,
168
- workstreamTitle: params.latestWorkstreamRecord.title ?? params.workstream.title,
169
- workstreamMode: params.workstream.mode,
170
- ...(params.workstream.coreType ? { coreType: params.workstream.coreType } : {}),
167
+ threadId: params.threadIdString,
168
+ threadTitle: params.latestThreadRecord.title ?? params.thread.title,
169
+ threadType: params.thread.type,
170
+ ...(params.thread.threadType ? { coreType: params.thread.threadType } : {}),
171
171
  userMessageText,
172
172
  assistantSummary: conversationSummary,
173
173
  messageId: params.referenceUserMessageId,
@@ -186,7 +186,7 @@ export async function runPostTurnSideEffects(params: PostTurnSideEffectsParams):
186
186
 
187
187
  await enqueuePostChatOrgAction({
188
188
  orgId: params.orgIdString,
189
- workstreamId: params.workstreamIdString,
189
+ threadId: params.threadIdString,
190
190
  sourceId: params.referenceUserMessageId,
191
191
  sourceCreatedAt,
192
192
  conversationSummary,
@@ -203,7 +203,7 @@ export async function runPostTurnSideEffects(params: PostTurnSideEffectsParams):
203
203
  }
204
204
  }
205
205
 
206
- if (shouldEnqueueRegularDigestForWorkstream({ onboardingActive: params.onboardingActive, turnCount })) {
206
+ if (shouldEnqueueRegularDigestForThread({ onboardingActive: params.onboardingActive, turnCount })) {
207
207
  await safeEnqueue(() => enqueueRegularChatMemoryDigest({ orgId: params.orgIdString }), {
208
208
  operationName: 'regular chat memory digest enqueue',
209
209
  })
@@ -1,9 +1,9 @@
1
1
  import type { ToolSet } from 'ai'
2
2
  import { z } from 'zod'
3
3
 
4
- import type { CoreWorkstreamProfile } from '../config/agent-defaults'
4
+ import type { CoreThreadProfile } from '../config/agent-defaults'
5
5
  import type { AgentFactory, AgentRuntimeConfigProvider, AgentToolBuilder } from '../config/agent-types'
6
- import type { LotaWorkstreamConfig, WorkstreamBootstrapWelcomeConfig } from '../config/workstream-defaults'
6
+ import type { LotaThreadConfig, ThreadBootstrapWelcomeConfig } from '../config/thread-defaults'
7
7
  import type { RecordIdRef } from '../db/record-id'
8
8
  import type { NotificationService } from '../services/notification.service'
9
9
  import { isRecord } from '../utils/string'
@@ -144,24 +144,27 @@ function isSocialChatConfig(value: unknown): value is LotaRuntimeSocialChatConfi
144
144
  return true
145
145
  }
146
146
 
147
- const workstreamBootstrapWelcomeConfigSchema = z.object({
148
- directAgentId: z.string().trim().min(1),
149
- buildMessageText: z.custom<WorkstreamBootstrapWelcomeConfig['buildMessageText']>(isFunction, {
147
+ const threadBootstrapWelcomeConfigSchema = z.object({
148
+ defaultAgentId: z.string().trim().min(1),
149
+ buildMessageText: z.custom<ThreadBootstrapWelcomeConfig['buildMessageText']>(isFunction, {
150
150
  error: 'onboardingWelcome.buildMessageText must be a function',
151
151
  }),
152
152
  })
153
153
 
154
- const workstreamConfigSchema = z.object({
155
- bootstrap: z
156
- .object({
157
- onboardingDirectAgents: z.array(z.string().trim().min(1)).optional(),
158
- completedDirectAgents: z.array(z.string().trim().min(1)).optional(),
159
- coreTypesAfterOnboarding: z.array(z.string().trim().min(1)).optional(),
160
- ensureDefaultGroupOnCompleted: z.boolean().optional(),
161
- onboardingWelcome: workstreamBootstrapWelcomeConfigSchema.optional(),
162
- })
163
- .optional(),
164
- })
154
+ const threadConfigSchema = z
155
+ .object({
156
+ bootstrap: z
157
+ .object({
158
+ onboardingDefaultAgents: z.array(z.string().trim().min(1)).optional(),
159
+ completedDefaultAgents: z.array(z.string().trim().min(1)).optional(),
160
+ threadTypesAfterOnboarding: z.array(z.string().trim().min(1)).optional(),
161
+ ensureDefaultGroupOnCompleted: z.boolean().optional(),
162
+ onboardingWelcome: threadBootstrapWelcomeConfigSchema.optional(),
163
+ })
164
+ .strict()
165
+ .optional(),
166
+ })
167
+ .strict()
165
168
 
166
169
  const agentsConfigSchema = z
167
170
  .object({
@@ -178,9 +181,9 @@ const agentsConfigSchema = z
178
181
  .optional(),
179
182
  routerModelId: z.string().trim().min(1).optional(),
180
183
  teamConsultParticipants: z.array(z.string().trim().min(1)),
181
- getCoreWorkstreamProfile: z
182
- .custom<(coreType: string) => CoreWorkstreamProfile>(isFunction, {
183
- error: 'agents.getCoreWorkstreamProfile must be a function',
184
+ getCoreThreadProfile: z
185
+ .custom<(coreType: string) => CoreThreadProfile>(isFunction, {
186
+ error: 'agents.getCoreThreadProfile must be a function',
184
187
  })
185
188
  .optional(),
186
189
  createAgent: z
@@ -249,7 +252,7 @@ export const LotaRuntimeConfigSchema = z.object({
249
252
  embeddingCacheTtlSeconds: z.coerce.number().int().positive().default(7200),
250
253
  })
251
254
  .default({ searchK: 6, embeddingCacheTtlSeconds: 7200 }),
252
- workstreams: workstreamConfigSchema.default({}),
255
+ threads: threadConfigSchema.default({}),
253
256
  agents: agentsConfigSchema,
254
257
  toolProviders: z.custom<Record<string, unknown>>(isToolProviderRecord).optional(),
255
258
  extraSchemaFiles: z.array(z.custom<string | URL>(isStringOrUrl)).optional(),
@@ -319,4 +322,4 @@ export function parseWorkerBootstrapEnv(env: Record<string, string | undefined>)
319
322
  return WORKER_BOOTSTRAP_ENV_SCHEMA.parse(env)
320
323
  }
321
324
 
322
- export type { LotaWorkstreamConfig }
325
+ export type { LotaThreadConfig }
@@ -74,7 +74,7 @@ export interface LotaRuntimeTeamThinkToolsParams {
74
74
  workspaceId: RecordIdRef
75
75
  userId: RecordIdRef
76
76
  workspaceIdString: string
77
- workstreamId: RecordIdRef
77
+ threadId: RecordIdRef
78
78
  githubInstalled: boolean
79
79
  provideRepoTool: boolean
80
80
  availableUploads: ReadableUploadMetadata[]
@@ -90,7 +90,7 @@ export interface LotaRuntimePlanEventEnvelope {
90
90
  nodeSpec?: PlanNodeSpecRecord
91
91
  nodeRun?: PlanNodeRunRecord
92
92
  organizationId: string
93
- workstreamId: string
93
+ threadId: string
94
94
  runId: string
95
95
  planSpecId: string
96
96
  userId?: string
@@ -102,8 +102,8 @@ export interface LotaRuntimePlanEventAdapter {
102
102
  }
103
103
 
104
104
  export interface BuildContextParams {
105
- workstream: unknown
106
- workstreamRef: RecordIdRef
105
+ thread: unknown
106
+ threadRef: RecordIdRef
107
107
  orgRef: RecordIdRef
108
108
  userRef: RecordIdRef
109
109
  userName?: string | null
@@ -123,15 +123,15 @@ export interface BuildContextParams {
123
123
  }
124
124
 
125
125
  export interface AfterTurnParams {
126
- workstream: unknown
127
- workstreamRef: RecordIdRef
126
+ thread: unknown
127
+ threadRef: RecordIdRef
128
128
  orgRef: RecordIdRef
129
129
  userRef: RecordIdRef
130
130
  userName?: string | null
131
131
  onboardingActive: boolean
132
132
  referenceUserMessage: unknown
133
133
  assistantMessages: unknown[]
134
- latestWorkstreamRecord: unknown
134
+ latestThreadRecord: unknown
135
135
  context: Record<string, unknown> | null
136
136
  [key: string]: unknown
137
137
  }
@@ -139,8 +139,8 @@ export interface AfterTurnParams {
139
139
  export interface ResolveAgentParams {
140
140
  agentId: string
141
141
  mode: string
142
- workstream: unknown
143
- workstreamRef: RecordIdRef
142
+ thread: unknown
143
+ threadRef: RecordIdRef
144
144
  orgRef: RecordIdRef
145
145
  userRef: RecordIdRef
146
146
  userName?: string | null
@@ -154,8 +154,8 @@ export interface ResolveAgentParams {
154
154
  }
155
155
 
156
156
  export interface BuildExtraInstructionSectionsParams {
157
- workstream: unknown
158
- workstreamRef: RecordIdRef
157
+ thread: unknown
158
+ threadRef: RecordIdRef
159
159
  orgRef: RecordIdRef
160
160
  userRef: RecordIdRef
161
161
  userName?: string | null
@@ -59,8 +59,8 @@ export function withLoggedSocialToolSet(
59
59
 
60
60
  export async function runSocialAgentTurn(params: {
61
61
  agentId: string
62
- mode: 'fixedWorkstreamMode' | 'workstreamMode'
63
- workstreamMode: 'group'
62
+ mode: 'fixedThreadMode' | 'threadMode'
63
+ threadType: 'group'
64
64
  onboardingActive: boolean
65
65
  linearInstalled: boolean
66
66
  systemWorkspaceDetails?: string
@@ -75,7 +75,7 @@ export async function runSocialAgentTurn(params: {
75
75
  }): Promise<{ text: string; displayName: string }> {
76
76
  const runtimeConfig = getAgentRuntimeConfig({
77
77
  agentId: params.agentId,
78
- workstreamMode: params.workstreamMode,
78
+ threadType: params.threadType,
79
79
  mode: params.mode,
80
80
  onboardingActive: params.onboardingActive,
81
81
  linearInstalled: params.linearInstalled,
@@ -2,7 +2,7 @@ import { stripSlackToolExecutionNoticeMarkdown } from '@lota-sdk/shared'
2
2
  import type { Message, Thread } from 'chat'
3
3
 
4
4
  import type { SocialChatHistoryMessage } from '../services/social-chat-history.service'
5
- import { toHistoryMessages, toOptionalTrimmedString } from './workstream-chat-helpers'
5
+ import { toHistoryMessages, toOptionalTrimmedString } from './thread-chat-helpers'
6
6
 
7
7
  export function readSlackAuthorName(message: Message): string | undefined {
8
8
  return (