@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
@@ -1,7 +1,7 @@
1
1
  import type {
2
2
  ChatMessage,
3
+ ExecutionPlanQueryArgs,
3
4
  ExecutionPlanToolResultData,
4
- GetActiveExecutionPlanArgs,
5
5
  ListExecutionPlansSummary,
6
6
  ListExecutionPlansToolResultData,
7
7
  PlanEventRecord,
@@ -9,7 +9,6 @@ import type {
9
9
  PlanNodeRunRecord,
10
10
  PlanNodeSpecRecord,
11
11
  PlanSpecRecord,
12
- ResumeExecutionPlanRunArgs,
13
12
  SerializableExecutionPlan,
14
13
  SubmitPlanTurnResultArgs,
15
14
  SubmitExecutionNodeResultArgs,
@@ -30,7 +29,7 @@ import { databaseService } from '../db/service'
30
29
  import type { DatabaseTransaction } from '../db/service'
31
30
  import { TABLES } from '../db/tables'
32
31
  import { readApprovalContinuationResponse } from '../runtime/approval-continuation'
33
- import { extractMessageText } from '../runtime/workstream-chat-helpers'
32
+ import { extractMessageText } from '../runtime/thread-chat-helpers'
34
33
  import { toDatabaseDateTime } from '../utils/date-time'
35
34
  import { ownershipDispatcherService } from './ownership-dispatcher.service'
36
35
  import { planBuilderService } from './plan-builder.service'
@@ -50,7 +49,7 @@ function aggregateBlockingIssues(issues: Array<{ code: string; message: string }
50
49
  function toSpecData(spec: PlanSpecRecord, patch: Partial<PlanSpecRecord> & { replacedSpecId?: RecordIdInput | null }) {
51
50
  return {
52
51
  organizationId: ensureRecordId(spec.organizationId, TABLES.ORGANIZATION),
53
- workstreamId: ensureRecordId(spec.workstreamId, TABLES.WORKSTREAM),
52
+ threadId: ensureRecordId(spec.threadId, TABLES.THREAD),
54
53
  title: patch.title ?? spec.title,
55
54
  objective: patch.objective ?? spec.objective,
56
55
  version: patch.version ?? spec.version,
@@ -105,7 +104,7 @@ function buildApprovalResponseFromMessages(
105
104
 
106
105
  function buildCompiledSpecCreateData(params: {
107
106
  organizationId: RecordIdInput
108
- workstreamId: RecordIdInput
107
+ threadId: RecordIdInput
109
108
  leadAgentId: string
110
109
  compiled: ReturnType<typeof planCompilerService.compile>
111
110
  version: number
@@ -114,7 +113,7 @@ function buildCompiledSpecCreateData(params: {
114
113
  }) {
115
114
  return {
116
115
  organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
117
- workstreamId: ensureRecordId(params.workstreamId, TABLES.WORKSTREAM),
116
+ threadId: ensureRecordId(params.threadId, TABLES.THREAD),
118
117
  title: params.compiled.draft.title,
119
118
  objective: params.compiled.draft.objective,
120
119
  version: params.version,
@@ -136,21 +135,21 @@ function buildCompiledSpecCreateData(params: {
136
135
  }
137
136
 
138
137
  class ExecutionPlanService {
139
- async hasActivePlan(workstreamId: RecordIdInput): Promise<boolean> {
140
- return (await planRunService.getActiveRunRecord(workstreamId)) !== null
138
+ async hasActivePlan(threadId: RecordIdInput): Promise<boolean> {
139
+ return (await planRunService.getActiveRunRecord(threadId)) !== null
141
140
  }
142
141
 
143
- async getActivePlanForWorkstream(
144
- workstreamId: RecordIdInput,
145
- options?: Partial<GetActiveExecutionPlanArgs> & { runId?: RecordIdInput },
142
+ async getActivePlanForThread(
143
+ threadId: RecordIdInput,
144
+ options?: Partial<ExecutionPlanQueryArgs> & { runId?: RecordIdInput },
146
145
  ): Promise<SerializableExecutionPlan | null> {
147
- const plans = await this.getActivePlansForWorkstream(workstreamId, options)
146
+ const plans = await this.getActivePlansForThread(threadId, options)
148
147
  return plans[0] ?? null
149
148
  }
150
149
 
151
- async getActivePlansForWorkstream(
152
- workstreamId: RecordIdInput,
153
- options?: Partial<GetActiveExecutionPlanArgs> & { runId?: RecordIdInput },
150
+ async getActivePlansForThread(
151
+ threadId: RecordIdInput,
152
+ options?: Partial<ExecutionPlanQueryArgs> & { runId?: RecordIdInput },
154
153
  ): Promise<SerializableExecutionPlan[]> {
155
154
  if (options?.runId) {
156
155
  const run = await planRunService.getRunById(options.runId)
@@ -165,7 +164,7 @@ class ExecutionPlanService {
165
164
  ]
166
165
  }
167
166
 
168
- const runs = await planRunService.getActiveRunRecords(workstreamId)
167
+ const runs = await planRunService.getActiveRunRecords(threadId)
169
168
  if (runs.length === 0) return []
170
169
 
171
170
  return await Promise.all(
@@ -181,8 +180,8 @@ class ExecutionPlanService {
181
180
  )
182
181
  }
183
182
 
184
- async listActivePlanSummaries(workstreamId: RecordIdInput): Promise<ListExecutionPlansToolResultData> {
185
- const runs = await planRunService.getActiveRunRecords(workstreamId)
183
+ async listActivePlanSummaries(threadId: RecordIdInput): Promise<ListExecutionPlansToolResultData> {
184
+ const runs = await planRunService.getActiveRunRecords(threadId)
186
185
  const plans: ListExecutionPlansSummary[] = await Promise.all(
187
186
  runs.map(async (run) => {
188
187
  const spec = await planRunService.getPlanSpecById(run.planSpecId)
@@ -202,7 +201,7 @@ class ExecutionPlanService {
202
201
  }
203
202
 
204
203
  async getActivePlanToolResult(params: {
205
- workstreamId: RecordIdInput
204
+ threadId: RecordIdInput
206
205
  runId?: string
207
206
  includeEvents?: boolean
208
207
  includeArtifacts?: boolean
@@ -225,36 +224,26 @@ class ExecutionPlanService {
225
224
  return buildExecutionPlanToolResult({ action: 'loaded', plan, message: `Loaded execution run "${plan.title}".` })
226
225
  }
227
226
 
228
- const runs = await planRunService.getActiveRunRecords(params.workstreamId)
227
+ const runs = await planRunService.getActiveRunRecords(params.threadId)
229
228
  if (runs.length === 0) {
230
229
  return buildExecutionPlanToolResult({ action: 'none', plan: null, message: 'No active execution run.' })
231
230
  }
232
231
 
233
232
  const plan = await planRunService.toSerializablePlan(runs[0], serializeOptions)
234
- const planSummaries = await Promise.all(
235
- runs.map(async (run) => {
236
- const spec = await planRunService.getPlanSpecById(run.planSpecId)
237
- return { runId: recordIdToString(run.id, TABLES.PLAN_RUN), title: spec.title, status: run.status }
238
- }),
239
- )
240
233
 
241
- return {
242
- ...buildExecutionPlanToolResult({
243
- action: 'loaded',
244
- plan,
245
- message:
246
- runs.length === 1
247
- ? `Loaded execution run "${plan.title}".`
248
- : `Loaded ${runs.length} active execution runs. Showing most recent: "${plan.title}".`,
249
- }),
250
- planCount: runs.length,
251
- planSummaries,
252
- }
234
+ return buildExecutionPlanToolResult({
235
+ action: 'loaded',
236
+ plan,
237
+ message:
238
+ runs.length === 1
239
+ ? `Loaded execution run "${plan.title}".`
240
+ : `Loaded ${runs.length} active execution runs. Showing most recent: "${plan.title}".`,
241
+ })
253
242
  }
254
243
 
255
244
  async createPlan(params: {
256
245
  organizationId: RecordIdInput
257
- workstreamId: RecordIdInput
246
+ threadId: RecordIdInput
258
247
  leadAgentId: string
259
248
  input: PlanDraft
260
249
  }): Promise<ExecutionPlanToolResultData> {
@@ -277,7 +266,7 @@ class ExecutionPlanService {
277
266
  .content(
278
267
  buildCompiledSpecCreateData({
279
268
  organizationId: params.organizationId,
280
- workstreamId: params.workstreamId,
269
+ threadId: params.threadId,
281
270
  leadAgentId: params.leadAgentId,
282
271
  compiled,
283
272
  version: 1,
@@ -290,7 +279,7 @@ class ExecutionPlanService {
290
279
  runId,
291
280
  spec,
292
281
  organizationId: params.organizationId,
293
- workstreamId: params.workstreamId,
282
+ threadId: params.threadId,
294
283
  leadAgentId: params.leadAgentId,
295
284
  nodes: compiled.nodes,
296
285
  emittedEvents,
@@ -307,7 +296,7 @@ class ExecutionPlanService {
307
296
  if (compiled.draft.schedule) {
308
297
  const schedule = await planSchedulerService.createSchedule({
309
298
  organizationId: params.organizationId,
310
- workstreamId: params.workstreamId,
299
+ threadId: params.threadId,
311
300
  planSpecId: specId,
312
301
  runId,
313
302
  scheduleSpec: compiled.draft.schedule,
@@ -330,17 +319,17 @@ class ExecutionPlanService {
330
319
  }
331
320
 
332
321
  async replacePlan(params: {
333
- workstreamId: RecordIdInput
322
+ threadId: RecordIdInput
334
323
  organizationId: RecordIdInput
335
324
  leadAgentId: string
336
325
  input: PlanDraft & { runId: string; reason: string }
337
326
  }): Promise<ExecutionPlanToolResultData> {
338
327
  const activeRun = await planRunService.getRunById(params.input.runId)
339
- const resolvedWorkstreamId = activeRun.workstreamId
328
+ const resolvedThreadId = activeRun.threadId
340
329
 
341
- const activeRuns = await planRunService.getActiveRunRecords(resolvedWorkstreamId)
330
+ const activeRuns = await planRunService.getActiveRunRecords(resolvedThreadId)
342
331
  if (activeRuns.length === 0) {
343
- throw new Error('No active execution run exists for this workstream.')
332
+ throw new Error('No active execution run exists for this thread.')
344
333
  }
345
334
  if (!activeRuns.some((run) => recordIdToString(run.id, TABLES.PLAN_RUN) === params.input.runId)) {
346
335
  throw new Error('Only an active execution run can be replaced.')
@@ -399,7 +388,7 @@ class ExecutionPlanService {
399
388
  .content(
400
389
  buildCompiledSpecCreateData({
401
390
  organizationId: params.organizationId,
402
- workstreamId: resolvedWorkstreamId,
391
+ threadId: resolvedThreadId,
403
392
  leadAgentId: params.leadAgentId,
404
393
  compiled,
405
394
  version: supersededSpec.version + 1,
@@ -413,7 +402,7 @@ class ExecutionPlanService {
413
402
  runId,
414
403
  spec,
415
404
  organizationId: params.organizationId,
416
- workstreamId: resolvedWorkstreamId,
405
+ threadId: resolvedThreadId,
417
406
  leadAgentId: params.leadAgentId,
418
407
  nodes: compiled.nodes,
419
408
  emittedEvents,
@@ -440,28 +429,29 @@ class ExecutionPlanService {
440
429
  }
441
430
 
442
431
  async submitNodeResult(params: {
443
- workstreamId: RecordIdInput
432
+ threadId: RecordIdInput
444
433
  emittedBy: string
445
434
  input: SubmitExecutionNodeResultArgs
446
435
  }): Promise<ExecutionPlanToolResultData> {
436
+ const { runId, nodeId, ...result } = params.input
447
437
  return await this.submitPlanTurnResult({
448
- workstreamId: params.workstreamId,
438
+ threadId: params.threadId,
449
439
  emittedBy: params.emittedBy,
450
- runId: params.input.runId,
451
- nodeId: params.input.nodeId,
452
- input: params.input.result,
440
+ runId,
441
+ nodeId,
442
+ input: result,
453
443
  })
454
444
  }
455
445
 
456
446
  async submitPlanTurnResult(params: {
457
- workstreamId: RecordIdInput
447
+ threadId: RecordIdInput
458
448
  runId: string
459
449
  nodeId: string
460
450
  emittedBy: string
461
451
  input: SubmitPlanTurnResultArgs
462
452
  }): Promise<ExecutionPlanToolResultData> {
463
453
  const result = await planExecutorService.submitNodeResult({
464
- workstreamId: params.workstreamId,
454
+ threadId: params.threadId,
465
455
  runId: params.runId,
466
456
  nodeId: params.nodeId,
467
457
  emittedBy: params.emittedBy,
@@ -476,17 +466,16 @@ class ExecutionPlanService {
476
466
  action: result.action,
477
467
  plan,
478
468
  message: result.message ?? `Submitted result for node "${params.nodeId}".`,
479
- changedNodeId: result.changedNodeId ?? undefined,
480
469
  })
481
470
  }
482
471
 
483
472
  async resumeRun(params: {
484
- workstreamId: RecordIdInput
473
+ threadId: RecordIdInput
485
474
  emittedBy: string
486
- input: ResumeExecutionPlanRunArgs
475
+ input: { runId: string }
487
476
  }): Promise<ExecutionPlanToolResultData> {
488
477
  const result = await planExecutorService.resumeRun({
489
- workstreamId: params.workstreamId,
478
+ threadId: params.threadId,
490
479
  runId: params.input.runId,
491
480
  emittedBy: params.emittedBy,
492
481
  })
@@ -499,12 +488,11 @@ class ExecutionPlanService {
499
488
  action: result.action,
500
489
  plan,
501
490
  message: result.message ?? `Resumed execution run "${params.input.runId}".`,
502
- changedNodeId: result.changedNodeId ?? undefined,
503
491
  })
504
492
  }
505
493
 
506
494
  async applyApprovalResponseFromMessages(params: {
507
- workstreamId: RecordIdInput
495
+ threadId: RecordIdInput
508
496
  approvalMessages: ChatMessage[]
509
497
  respondedBy: string
510
498
  }): Promise<SerializableExecutionPlan | null> {
@@ -512,22 +500,22 @@ class ExecutionPlanService {
512
500
  if (!approvalResponse) return null
513
501
 
514
502
  return await this.respondToApproval({
515
- workstreamId: params.workstreamId,
503
+ threadId: params.threadId,
516
504
  emittedBy: params.respondedBy,
517
505
  input: approvalResponse,
518
506
  })
519
507
  }
520
508
 
521
509
  async respondToApproval(params: {
522
- workstreamId: RecordIdInput
510
+ threadId: RecordIdInput
523
511
  emittedBy: string
524
512
  input: { approvalId: string; response: Record<string, unknown>; approvalMessageId?: string }
525
513
  }): Promise<SerializableExecutionPlan | null> {
526
- const run = await planRunService.getActiveRunRecord(params.workstreamId)
514
+ const run = await planRunService.getActiveRunRecord(params.threadId)
527
515
  if (!run) return null
528
516
 
529
517
  const plan = await planExecutorService.submitHumanNodeResponse({
530
- workstreamId: params.workstreamId,
518
+ threadId: params.threadId,
531
519
  approvalId: params.input.approvalId,
532
520
  respondedBy: params.emittedBy,
533
521
  response: params.input.response,
@@ -539,11 +527,11 @@ class ExecutionPlanService {
539
527
  }
540
528
 
541
529
  async applyHumanInputFromUserMessage(params: {
542
- workstreamId: RecordIdInput
530
+ threadId: RecordIdInput
543
531
  message: ChatMessage
544
532
  respondedBy: string
545
533
  }): Promise<SerializableExecutionPlan | null> {
546
- const run = await planRunService.getActiveRunRecord(params.workstreamId)
534
+ const run = await planRunService.getActiveRunRecord(params.threadId)
547
535
  if (!run || run.status !== 'awaiting-human' || !run.waitingNodeId) return null
548
536
 
549
537
  const nodeSpec = await planRunService.getNodeSpecByNodeId(run.planSpecId, run.waitingNodeId)
@@ -565,7 +553,7 @@ class ExecutionPlanService {
565
553
  } satisfies Record<string, unknown>
566
554
 
567
555
  const plan = await planExecutorService.submitHumanNodeResponse({
568
- workstreamId: params.workstreamId,
556
+ threadId: params.threadId,
569
557
  respondedBy: params.respondedBy,
570
558
  response,
571
559
  approvalMessageId: params.message.id,
@@ -677,7 +665,7 @@ class ExecutionPlanService {
677
665
  runId: RecordIdInput
678
666
  spec: PlanSpecRecord
679
667
  organizationId: RecordIdInput
680
- workstreamId: RecordIdInput
668
+ threadId: RecordIdInput
681
669
  leadAgentId: string
682
670
  nodes: CompiledPlanNode[]
683
671
  emittedEvents: PlanEventRecord[]
@@ -694,7 +682,7 @@ class ExecutionPlanService {
694
682
  .content({
695
683
  planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
696
684
  organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
697
- workstreamId: ensureRecordId(params.workstreamId, TABLES.WORKSTREAM),
685
+ threadId: ensureRecordId(params.threadId, TABLES.THREAD),
698
686
  leadAgentId: params.leadAgentId,
699
687
  status: 'running',
700
688
  readyNodeIds: [],
@@ -55,7 +55,7 @@ class GlobalOrchestratorService {
55
55
  return 'skip'
56
56
  }
57
57
 
58
- async routeGraphFull(params: { workstreamId: string; runId: string }): Promise<void> {
58
+ async routeGraphFull(params: { threadId: string; runId: string }): Promise<void> {
59
59
  const MAX_ROUNDS = 32
60
60
  const STRUCTURAL_TYPES = new Set(['switch', 'join', 'deliberation-fork'])
61
61
 
@@ -105,7 +105,7 @@ class GlobalOrchestratorService {
105
105
  if (!ns || ns.owner.executorType !== 'agent') continue
106
106
  await enqueuePlanAgentHeartbeatWake({
107
107
  organizationId: recordIdToString(updatedRunForWake.organizationId, TABLES.ORGANIZATION),
108
- workstreamId: recordIdToString(updatedRunForWake.workstreamId, TABLES.WORKSTREAM),
108
+ threadId: recordIdToString(updatedRunForWake.threadId, TABLES.THREAD),
109
109
  runId: recordIdToString(updatedRunForWake.id, TABLES.PLAN_RUN),
110
110
  nodeId: nodeRun.nodeId,
111
111
  agentId: ns.owner.ref,
@@ -140,7 +140,7 @@ class GlobalOrchestratorService {
140
140
  }),
141
141
  )
142
142
 
143
- const workstreamId = recordIdToString(updatedRun.workstreamId, TABLES.WORKSTREAM)
143
+ const threadId = recordIdToString(updatedRun.threadId, TABLES.THREAD)
144
144
  const runId = recordIdToString(updatedRun.id, TABLES.PLAN_RUN)
145
145
 
146
146
  // Submit results sequentially (each triggers syncRunGraph internally)
@@ -151,7 +151,7 @@ class GlobalOrchestratorService {
151
151
 
152
152
  if (settled.status === 'fulfilled') {
153
153
  await planExecutorService.submitNodeResult({
154
- workstreamId,
154
+ threadId,
155
155
  runId,
156
156
  nodeId: settled.value.nodeId,
157
157
  emittedBy: settled.value.ownerRef,
@@ -160,7 +160,7 @@ class GlobalOrchestratorService {
160
160
  } else {
161
161
  serverLogger.warn`routeGraphFull: dispatch failed for node "${nodeRun.nodeId}": ${settled.reason}`
162
162
  await planExecutorService.blockNodeOnDispatchFailure({
163
- workstreamId,
163
+ threadId,
164
164
  runId,
165
165
  nodeId: nodeRun.nodeId,
166
166
  emittedBy: nodeSpecRecord?.owner.ref ?? 'unknown',
@@ -35,10 +35,10 @@ export * from './skill-resolver.service'
35
35
  export * from './social-chat-history.service'
36
36
  export * from './system-executor.service'
37
37
  export * from './user.service'
38
- export * from './workstream-message.service'
39
- export * from './workstream.types'
40
- export * from './workstream-title.service'
41
- export * from './workstream-turn'
42
- export * from './workstream-plan-registry.service'
43
- export * from './workstream.service'
38
+ export * from './thread-message.service'
39
+ export * from './thread.types'
40
+ export * from './thread-title.service'
41
+ export * from './thread-turn'
42
+ export * from './thread-plan-registry.service'
43
+ export * from './thread.service'
44
44
  export * from './write-intent-validator.service'
@@ -209,7 +209,7 @@ class MemoryService {
209
209
  MAX_CONVERSATION_MEMORY_BLOCK_CHARS,
210
210
  )
211
211
  if (normalizedMemoryBlock) {
212
- messages.push({ role: 'user', content: `Workstream memory block:\n${normalizedMemoryBlock}` })
212
+ messages.push({ role: 'user', content: `Thread memory block:\n${normalizedMemoryBlock}` })
213
213
  }
214
214
 
215
215
  const normalizedAttachmentContext = this.normalizeConversationText(
@@ -20,11 +20,11 @@ class MonitoringWindowService {
20
20
  nodeId: string
21
21
  config: MonitoringWindowConfig
22
22
  organizationId: string
23
- workstreamId: string
23
+ threadId: string
24
24
  }): Promise<void> {
25
25
  await planSchedulerService.createSchedule({
26
26
  organizationId: params.organizationId,
27
- workstreamId: params.workstreamId,
27
+ threadId: params.threadId,
28
28
  runId: params.runId,
29
29
  nodeId: params.nodeId,
30
30
  scheduleSpec: {
@@ -1,13 +1,13 @@
1
1
  import type { RecordIdRef } from '../db/record-id'
2
2
  import { ensureRecordId } from '../db/record-id'
3
3
  import { TABLES } from '../db/tables'
4
- import { extractMessageText } from '../runtime/workstream-chat-helpers'
5
- import { workstreamMessageService } from './workstream-message.service'
4
+ import { extractMessageText } from '../runtime/thread-chat-helpers'
5
+ import { threadMessageService } from './thread-message.service'
6
6
 
7
7
  const APPROVAL_VERIFICATION_MESSAGE_WINDOW = 20
8
8
 
9
9
  type VerifyMutatingApproval = (params: {
10
- workstreamId: string
10
+ threadId: string
11
11
  approvalReason: string
12
12
  approvalToken: string
13
13
  approvalMessageId?: string
@@ -58,8 +58,8 @@ function messageContainsExactApproval(messageText: string, approvalToken: string
58
58
  return extracted.token === approvalToken && extracted.reason === approvalReason
59
59
  }
60
60
 
61
- async function verifyMutatingApprovalForWorkstream(
62
- workstreamId: RecordIdRef,
61
+ async function verifyMutatingApprovalForThread(
62
+ threadId: RecordIdRef,
63
63
  params: { approvalReason: string; approvalToken: string; approvalMessageId?: string },
64
64
  ): Promise<{ ok: true } | { ok: false; reason: string }> {
65
65
  const token = extractQuotedValue(params.approvalToken)
@@ -72,10 +72,7 @@ async function verifyMutatingApprovalForWorkstream(
72
72
  return { ok: false, reason: 'approvalMessageId cannot be empty when provided.' }
73
73
  }
74
74
 
75
- const recentMessages = await workstreamMessageService.listRecentMessages(
76
- workstreamId,
77
- APPROVAL_VERIFICATION_MESSAGE_WINDOW,
78
- )
75
+ const recentMessages = await threadMessageService.listRecentMessages(threadId, APPROVAL_VERIFICATION_MESSAGE_WINDOW)
79
76
  const userMessages = recentMessages.filter((message) => message.role === 'user')
80
77
  if (userMessages.length === 0) {
81
78
  return { ok: false, reason: 'No user message history available to verify mutating approval.' }
@@ -106,5 +103,5 @@ async function verifyMutatingApprovalForWorkstream(
106
103
  }
107
104
 
108
105
  export const verifyMutatingApproval: VerifyMutatingApproval = async (params) => {
109
- return verifyMutatingApprovalForWorkstream(ensureRecordId(params.workstreamId, TABLES.WORKSTREAM), params)
106
+ return verifyMutatingApprovalForThread(ensureRecordId(params.threadId, TABLES.THREAD), params)
110
107
  }
@@ -1,5 +1,4 @@
1
1
  import type {
2
- NodeResultQuality,
3
2
  PlanArtifactSubmission,
4
3
  PlanNodeResult,
5
4
  PlanNodeSpec,
@@ -86,7 +85,7 @@ class NodeWorkspaceService {
86
85
  })
87
86
  }
88
87
 
89
- finalize(workspace: NodeWorkspace): PlanNodeResult & { isComplete: boolean; quality: NodeResultQuality } {
88
+ finalize(workspace: NodeWorkspace): PlanNodeResult & { isComplete: boolean } {
90
89
  const artifacts: PlanArtifactSubmission[] = []
91
90
  let structuredOutput: Record<string, unknown> | undefined
92
91
  let allValidated = true
@@ -125,8 +124,6 @@ class NodeWorkspaceService {
125
124
  artifacts.push({
126
125
  name: spec.name,
127
126
  kind: spec.kind,
128
- pointer: `workspace://${workspace.nodeId}/${targetPath}`,
129
- schemaRef: spec.schemaRef,
130
127
  description: spec.description,
131
128
  payload:
132
129
  typeof entry.payload === 'string'
@@ -138,10 +135,14 @@ class NodeWorkspaceService {
138
135
  }
139
136
 
140
137
  const allRequiredPresent = requiredNames.size === presentRequired.size
141
- const quality: NodeResultQuality = allRequiredPresent && allValidated ? 'full' : 'partial'
142
- const isComplete = quality === 'full'
138
+ const isComplete = allRequiredPresent && allValidated
143
139
 
144
- return { structuredOutput, artifacts, quality, isComplete }
140
+ return {
141
+ notes: isComplete ? 'All deliverables completed.' : 'Partial deliverables completed.',
142
+ structuredOutput,
143
+ artifacts,
144
+ isComplete,
145
+ }
145
146
  }
146
147
 
147
148
  cleanup(workspace: NodeWorkspace): void {
@@ -2,7 +2,7 @@ import type { NotificationSeverity } from '@lota-sdk/shared'
2
2
 
3
3
  export interface NotificationPayload {
4
4
  organizationId: string
5
- workstreamId: string
5
+ threadId: string
6
6
  runId?: string
7
7
  nodeId?: string
8
8
  severity: NotificationSeverity
@@ -11,8 +11,8 @@ import { toIsoDateTimeString, toOptionalIsoDateTimeString } from '../utils/date-
11
11
  const organizationRecordSchema = z.object({
12
12
  id: recordIdSchema,
13
13
  name: z.string(),
14
- regularChatDigestLastWorkstreamCursorCreatedAt: z.coerce.date().optional(),
15
- regularChatDigestLastWorkstreamCursorId: z.string().optional(),
14
+ regularChatDigestLastThreadCursorCreatedAt: z.coerce.date().optional(),
15
+ regularChatDigestLastThreadCursorId: z.string().optional(),
16
16
  skillExtractionLastCursorId: z.string().optional(),
17
17
  skillExtractionLastCursorCreatedAt: z.coerce.date().optional(),
18
18
  createdAt: z.coerce.date(),
@@ -22,8 +22,8 @@ const organizationRecordSchema = z.object({
22
22
  const sdkOrganizationSchema = z.object({
23
23
  id: recordIdStringSchema,
24
24
  name: z.string(),
25
- regularChatDigestLastWorkstreamCursorCreatedAt: z.iso.datetime().nullable().optional(),
26
- regularChatDigestLastWorkstreamCursorId: z.string().nullable().optional(),
25
+ regularChatDigestLastThreadCursorCreatedAt: z.iso.datetime().nullable().optional(),
26
+ regularChatDigestLastThreadCursorId: z.string().nullable().optional(),
27
27
  skillExtractionLastCursorId: z.string().nullable().optional(),
28
28
  skillExtractionLastCursorCreatedAt: z.iso.datetime().nullable().optional(),
29
29
  createdAt: z.iso.datetime(),
@@ -51,10 +51,10 @@ class OrganizationService extends BaseService<typeof organizationRecordSchema> {
51
51
  return sdkOrganizationSchema.parse({
52
52
  id: recordIdToString(ensureRecordId(record.id as RecordIdInput, TABLES.ORGANIZATION), TABLES.ORGANIZATION),
53
53
  name: record.name,
54
- regularChatDigestLastWorkstreamCursorCreatedAt: toOptionalCursorTimestamp(
55
- record.regularChatDigestLastWorkstreamCursorCreatedAt,
54
+ regularChatDigestLastThreadCursorCreatedAt: toOptionalCursorTimestamp(
55
+ record.regularChatDigestLastThreadCursorCreatedAt,
56
56
  ),
57
- regularChatDigestLastWorkstreamCursorId: record.regularChatDigestLastWorkstreamCursorId ?? null,
57
+ regularChatDigestLastThreadCursorId: record.regularChatDigestLastThreadCursorId ?? null,
58
58
  skillExtractionLastCursorId: record.skillExtractionLastCursorId ?? null,
59
59
  skillExtractionLastCursorCreatedAt: toOptionalCursorTimestamp(record.skillExtractionLastCursorCreatedAt),
60
60
  createdAt: toIsoDateTimeString(record.createdAt),
@@ -101,8 +101,8 @@ class OrganizationService extends BaseService<typeof organizationRecordSchema> {
101
101
 
102
102
  async updateRegularChatDigestCursor(organizationId: RecordIdRef, cursor: BackgroundCursor): Promise<void> {
103
103
  await this.update(organizationId, {
104
- regularChatDigestLastWorkstreamCursorCreatedAt: cursor.createdAt,
105
- regularChatDigestLastWorkstreamCursorId: cursor.id,
104
+ regularChatDigestLastThreadCursorCreatedAt: cursor.createdAt,
105
+ regularChatDigestLastThreadCursorId: cursor.id,
106
106
  })
107
107
  }
108
108