@lota-sdk/core 0.1.24 → 0.1.25

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 (74) hide show
  1. package/package.json +2 -2
  2. package/src/ai/definitions.ts +5 -59
  3. package/src/ai-gateway/ai-gateway.ts +36 -28
  4. package/src/ai-gateway/cache-headers.ts +9 -0
  5. package/src/config/model-constants.ts +6 -2
  6. package/src/create-runtime.ts +1 -17
  7. package/src/db/memory-types.ts +13 -8
  8. package/src/db/memory.ts +74 -53
  9. package/src/queues/autonomous-job.queue.ts +1 -8
  10. package/src/queues/context-compaction.queue.ts +2 -2
  11. package/src/queues/index.ts +2 -6
  12. package/src/queues/organization-learning.queue.ts +78 -0
  13. package/src/queues/plan-agent-heartbeat.queue.ts +10 -16
  14. package/src/queues/title-generation.queue.ts +62 -0
  15. package/src/runtime/agent-prompt-context.ts +0 -18
  16. package/src/runtime/agent-runtime-policy.ts +9 -2
  17. package/src/runtime/context-compaction-constants.ts +4 -2
  18. package/src/runtime/context-compaction.ts +135 -118
  19. package/src/runtime/memory-pipeline.ts +70 -1
  20. package/src/runtime/memory-prompts-fact.ts +16 -0
  21. package/src/runtime/plugin-resolution.ts +3 -2
  22. package/src/runtime/plugin-types.ts +1 -42
  23. package/src/runtime/post-turn-side-effects.ts +212 -0
  24. package/src/runtime/runtime-config.ts +0 -13
  25. package/src/runtime/runtime-extensions.ts +10 -16
  26. package/src/runtime/runtime-worker-registry.ts +8 -19
  27. package/src/runtime/social-chat-agent-runner.ts +119 -0
  28. package/src/runtime/social-chat-history.ts +110 -0
  29. package/src/runtime/social-chat-prompts.ts +58 -0
  30. package/src/runtime/social-chat.ts +104 -340
  31. package/src/runtime/specialist-runner.ts +18 -0
  32. package/src/runtime/workstream-chat-helpers.ts +19 -0
  33. package/src/runtime/workstream-plan-turn.ts +195 -0
  34. package/src/runtime/workstream-state.ts +11 -8
  35. package/src/runtime/workstream-turn-context.ts +183 -0
  36. package/src/services/autonomous-job.service.ts +1 -8
  37. package/src/services/execution-plan.service.ts +205 -334
  38. package/src/services/index.ts +1 -4
  39. package/src/services/memory.service.ts +54 -44
  40. package/src/services/ownership-dispatcher.service.ts +2 -19
  41. package/src/services/plan-completion-side-effects.ts +80 -0
  42. package/src/services/plan-event-delivery.service.ts +1 -1
  43. package/src/services/plan-executor.service.ts +42 -190
  44. package/src/services/plan-node-spec.ts +60 -0
  45. package/src/services/plan-run-data.ts +88 -0
  46. package/src/services/plan-validator.service.ts +10 -8
  47. package/src/services/workstream-constants.ts +2 -0
  48. package/src/services/workstream-title.service.ts +1 -1
  49. package/src/services/workstream-turn-preparation.service.ts +208 -715
  50. package/src/services/workstream.service.ts +162 -192
  51. package/src/services/workstream.types.ts +12 -44
  52. package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -0
  53. package/src/tools/execution-plan.tool.ts +7 -6
  54. package/src/tools/remember-memory.tool.ts +7 -10
  55. package/src/tools/research-topic.tool.ts +1 -1
  56. package/src/tools/team-think.tool.ts +1 -1
  57. package/src/tools/user-questions.tool.ts +1 -1
  58. package/src/utils/autonomous-job-ids.ts +7 -0
  59. package/src/workers/organization-learning.worker.ts +31 -0
  60. package/src/workers/regular-chat-memory-digest.runner.ts +9 -3
  61. package/src/workers/skill-extraction.runner.ts +2 -2
  62. package/src/queues/recent-activity-title-refinement.queue.ts +0 -30
  63. package/src/queues/regular-chat-memory-digest.config.ts +0 -12
  64. package/src/queues/regular-chat-memory-digest.queue.ts +0 -34
  65. package/src/queues/skill-extraction.config.ts +0 -9
  66. package/src/queues/skill-extraction.queue.ts +0 -27
  67. package/src/queues/workstream-title-generation.queue.ts +0 -33
  68. package/src/services/context-enrichment.service.ts +0 -33
  69. package/src/services/coordination-registry.service.ts +0 -117
  70. package/src/services/domain-agent-executor.service.ts +0 -71
  71. package/src/services/memory-assessment.service.ts +0 -44
  72. package/src/services/playbook-registry.service.ts +0 -67
  73. package/src/workers/regular-chat-memory-digest.worker.ts +0 -22
  74. package/src/workers/skill-extraction.worker.ts +0 -22
@@ -0,0 +1,195 @@
1
+ import { SUBMIT_PLAN_TURN_RESULT_TOOL_NAME, withMessageCreatedAt } from '@lota-sdk/shared'
2
+ import type {
3
+ ChatMessage,
4
+ PlanArtifactSubmission,
5
+ PlanNodeHandoffContext,
6
+ PlanNodeRunRecord,
7
+ PlanNodeSpecRecord,
8
+ } from '@lota-sdk/shared'
9
+
10
+ import { buildCompletionCheckStructuredOutputHints } from './agent-runtime-policy'
11
+ import { mergeInstructionSections } from './instruction-sections'
12
+
13
+ export interface PlanTurnUpstreamHandoff {
14
+ nodeId: string
15
+ label: string
16
+ ownerRef: string
17
+ ownerType: PlanNodeSpecRecord['owner']['executorType']
18
+ handoffContext: PlanNodeHandoffContext
19
+ }
20
+
21
+ export interface WorkstreamPlanTurnContext {
22
+ runId: string
23
+ nodeId: string
24
+ planTitle: string
25
+ nodeSpec: PlanNodeSpecRecord
26
+ nodeRun: PlanNodeRunRecord
27
+ resolvedInput: Record<string, unknown>
28
+ inputArtifacts: PlanArtifactSubmission[]
29
+ upstreamHandoffs: PlanTurnUpstreamHandoff[]
30
+ }
31
+
32
+ function describePlanTurnDeliverable(deliverable: PlanNodeSpecRecord['deliverables'][number]): string {
33
+ return [
34
+ `- ${deliverable.name}`,
35
+ `kind=${deliverable.kind}`,
36
+ deliverable.required ? 'required' : 'optional',
37
+ deliverable.schemaRef ? `schemaRef=${deliverable.schemaRef}` : undefined,
38
+ deliverable.description ? `description=${deliverable.description}` : undefined,
39
+ ]
40
+ .filter(Boolean)
41
+ .join(' | ')
42
+ }
43
+
44
+ function describePlanTurnCompletionCheck(check: PlanNodeSpecRecord['completionChecks'][number]): string {
45
+ return [
46
+ `- ${check.description}`,
47
+ `type=${check.type}`,
48
+ check.blocking ? 'blocking' : 'warning',
49
+ Object.keys(check.config).length > 0 ? `config=${JSON.stringify(check.config)}` : undefined,
50
+ ]
51
+ .filter(Boolean)
52
+ .join(' | ')
53
+ }
54
+
55
+ function buildPlanTurnExecutionSection(planTurn: WorkstreamPlanTurnContext): string {
56
+ const payload = {
57
+ runId: planTurn.runId,
58
+ planTitle: planTurn.planTitle,
59
+ node: {
60
+ id: planTurn.nodeSpec.nodeId,
61
+ label: planTurn.nodeSpec.label,
62
+ owner: planTurn.nodeSpec.owner,
63
+ objective: planTurn.nodeSpec.objective,
64
+ instructions: planTurn.nodeSpec.instructions,
65
+ outputSchemaRef: planTurn.nodeSpec.outputSchemaRef ?? null,
66
+ deliverables: planTurn.nodeSpec.deliverables,
67
+ successCriteria: planTurn.nodeSpec.successCriteria,
68
+ completionChecks: planTurn.nodeSpec.completionChecks,
69
+ toolPolicy: planTurn.nodeSpec.toolPolicy,
70
+ contextPolicy: planTurn.nodeSpec.contextPolicy,
71
+ },
72
+ resolvedInput: planTurn.resolvedInput,
73
+ inputArtifacts: planTurn.inputArtifacts,
74
+ }
75
+
76
+ return [
77
+ '<plan-turn-execution>',
78
+ 'The runtime has activated a visible execution-plan node inside this workstream.',
79
+ `Complete node "${planTurn.nodeSpec.label}" for plan "${planTurn.planTitle}".`,
80
+ 'Use only the node contract, resolved input, input artifacts, and upstream handoff context provided here.',
81
+ 'Do not ask the user for more input and do not rely on unstated external context.',
82
+ 'Do not submit placeholders, partial work, or speculative outputs.',
83
+ 'Before submitting, satisfy every required deliverable, success criterion, and completion check for this node.',
84
+ 'Deliverables must use the exact artifact names and kinds declared in the node contract.',
85
+ 'If a deliverable declares schemaRef, include the same schemaRef and a payload that satisfies that schema.',
86
+ 'If outputSchemaRef is declared, structuredOutput must satisfy that schema before you submit.',
87
+ `When finished, call ${SUBMIT_PLAN_TURN_RESULT_TOOL_NAME} exactly once.`,
88
+ 'Always include durable handoffContext for downstream nodes when you submit the final result.',
89
+ 'Do not ask the user for confirmation and do not create or replace execution plans in this turn.',
90
+ JSON.stringify(payload, null, 2),
91
+ '</plan-turn-execution>',
92
+ ].join('\n')
93
+ }
94
+
95
+ function buildPlanTurnResultContractSection(planTurn: WorkstreamPlanTurnContext): string {
96
+ const requiredDeliverables = planTurn.nodeSpec.deliverables.filter((deliverable) => deliverable.required)
97
+ const completionCheckOutputHints = buildCompletionCheckStructuredOutputHints(planTurn.nodeSpec)
98
+ const deliverableLines =
99
+ planTurn.nodeSpec.deliverables.length > 0
100
+ ? planTurn.nodeSpec.deliverables.map(describePlanTurnDeliverable)
101
+ : ['- none']
102
+ const completionCheckLines =
103
+ planTurn.nodeSpec.completionChecks.length > 0
104
+ ? planTurn.nodeSpec.completionChecks.map(describePlanTurnCompletionCheck)
105
+ : ['- none']
106
+
107
+ return [
108
+ '<plan-turn-result-contract>',
109
+ `Call ${SUBMIT_PLAN_TURN_RESULT_TOOL_NAME} exactly once with a result object that passes node validation.`,
110
+ 'Validation is strict. Missing required artifacts, schema mismatches, or failed completion checks will fail the node run.',
111
+ `Required artifacts: ${requiredDeliverables.length > 0 ? requiredDeliverables.map((deliverable) => deliverable.name).join(', ') : 'none'}`,
112
+ `Structured output: ${
113
+ planTurn.nodeSpec.outputSchemaRef
114
+ ? `required and must match schema "${planTurn.nodeSpec.outputSchemaRef}"`
115
+ : 'optional unless needed by a completion check'
116
+ }`,
117
+ 'Deliverables:',
118
+ ...deliverableLines,
119
+ 'Completion checks:',
120
+ ...completionCheckLines,
121
+ ...(completionCheckOutputHints.length > 0
122
+ ? ['Structured output fields required by completion checks:', ...completionCheckOutputHints]
123
+ : []),
124
+ 'Include notes with a concise completion summary grounded in the submitted artifacts and structuredOutput.',
125
+ 'Always include handoffContext for downstream execution with a durable summary, key decisions, open questions, risks, recommendations, and references when relevant.',
126
+ '</plan-turn-result-contract>',
127
+ ].join('\n')
128
+ }
129
+
130
+ function buildUpstreamHandoffSection(upstreamHandoffs: PlanTurnUpstreamHandoff[]): string | undefined {
131
+ if (upstreamHandoffs.length === 0) {
132
+ return undefined
133
+ }
134
+
135
+ return [
136
+ '<upstream-handoff>',
137
+ JSON.stringify(
138
+ upstreamHandoffs.map((handoff) => ({
139
+ nodeId: handoff.nodeId,
140
+ label: handoff.label,
141
+ ownerRef: handoff.ownerRef,
142
+ ownerType: handoff.ownerType,
143
+ handoffContext: handoff.handoffContext,
144
+ })),
145
+ null,
146
+ 2,
147
+ ),
148
+ '</upstream-handoff>',
149
+ ].join('\n')
150
+ }
151
+
152
+ export function buildPlanTurnInstructionSections(planTurn: WorkstreamPlanTurnContext): string[] {
153
+ const upstreamHandoffSection = buildUpstreamHandoffSection(planTurn.upstreamHandoffs)
154
+ return (
155
+ mergeInstructionSections(
156
+ [buildPlanTurnExecutionSection(planTurn), buildPlanTurnResultContractSection(planTurn)],
157
+ upstreamHandoffSection ? [upstreamHandoffSection] : undefined,
158
+ ) ?? []
159
+ )
160
+ }
161
+
162
+ export function buildPlanTurnSubmitToolDescription(planTurn: WorkstreamPlanTurnContext): string {
163
+ const requiredArtifacts =
164
+ planTurn.nodeSpec.deliverables
165
+ .filter((deliverable) => deliverable.required)
166
+ .map((deliverable) => `${deliverable.name} (${deliverable.kind})`)
167
+ .join(', ') || 'none'
168
+
169
+ return [
170
+ 'Submit the final result for the active plan-triggered node turn.',
171
+ 'Call this exactly once when the node output is complete.',
172
+ `Required artifacts: ${requiredArtifacts}.`,
173
+ `Structured output: ${
174
+ planTurn.nodeSpec.outputSchemaRef ? `must satisfy ${planTurn.nodeSpec.outputSchemaRef}` : 'optional'
175
+ }.`,
176
+ 'Do not submit partial results. Include durable handoffContext for downstream nodes.',
177
+ ].join(' ')
178
+ }
179
+
180
+ export function buildPlanTurnPromptMessage(planTurn: WorkstreamPlanTurnContext): ChatMessage {
181
+ return withMessageCreatedAt(
182
+ {
183
+ id: Bun.randomUUIDv7(),
184
+ role: 'user',
185
+ parts: [
186
+ {
187
+ type: 'text',
188
+ text: `Execute the active plan node "${planTurn.nodeSpec.label}" now and submit the result with ${SUBMIT_PLAN_TURN_RESULT_TOOL_NAME}.`,
189
+ },
190
+ ],
191
+ metadata: { trigger: 'plan-turn', planRunId: planTurn.runId, planNodeId: planTurn.nodeId },
192
+ },
193
+ Date.now(),
194
+ )
195
+ }
@@ -116,10 +116,10 @@ export const WorkstreamStateDeltaSchema = z.object({
116
116
  conflicts: z
117
117
  .array(z.object({ newFact: z.string(), conflictsWith: z.string(), recommendation: z.string() }))
118
118
  .optional(),
119
- approvedBy: z.string().optional(),
120
- approvedAt: z.number().int().optional(),
121
- approvalMessageId: z.string().optional(),
122
- approvalNote: z.string().optional(),
119
+ approvedBy: z.string().nullable().optional(),
120
+ approvedAt: z.number().int().nullable().optional(),
121
+ approvalMessageId: z.string().nullable().optional(),
122
+ approvalNote: z.string().nullable().optional(),
123
123
  })
124
124
 
125
125
  export type WorkstreamStateDelta = z.infer<typeof WorkstreamStateDeltaSchema>
@@ -194,6 +194,7 @@ export function createEmptyStructuredWorkstreamStateDelta(): StructuredWorkstrea
194
194
  }
195
195
 
196
196
  export function parseStructuredWorkstreamStateDelta(value: unknown): WorkstreamStateDelta {
197
+ const inputRecord = typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : null
197
198
  const parsed = StructuredWorkstreamStateDeltaSchema.parse(value)
198
199
 
199
200
  return {
@@ -221,10 +222,12 @@ export function parseStructuredWorkstreamStateDelta(value: unknown): WorkstreamS
221
222
  ...(parsed.artifacts.length > 0 ? { artifacts: parsed.artifacts } : {}),
222
223
  ...(parsed.agentNote ? { agentNote: parsed.agentNote } : {}),
223
224
  ...(parsed.conflicts.length > 0 ? { conflicts: parsed.conflicts } : {}),
224
- ...(parsed.approvedBy !== null ? { approvedBy: parsed.approvedBy } : {}),
225
- ...(parsed.approvedAt !== null ? { approvedAt: parsed.approvedAt } : {}),
226
- ...(parsed.approvalMessageId !== null ? { approvalMessageId: parsed.approvalMessageId } : {}),
227
- ...(parsed.approvalNote !== null ? { approvalNote: parsed.approvalNote } : {}),
225
+ ...(inputRecord && Object.hasOwn(inputRecord, 'approvedBy') ? { approvedBy: parsed.approvedBy } : {}),
226
+ ...(inputRecord && Object.hasOwn(inputRecord, 'approvedAt') ? { approvedAt: parsed.approvedAt } : {}),
227
+ ...(inputRecord && Object.hasOwn(inputRecord, 'approvalMessageId')
228
+ ? { approvalMessageId: parsed.approvalMessageId }
229
+ : {}),
230
+ ...(inputRecord && Object.hasOwn(inputRecord, 'approvalNote') ? { approvalNote: parsed.approvalNote } : {}),
228
231
  }
229
232
  }
230
233
 
@@ -0,0 +1,183 @@
1
+ import type { RecordIdRef } from '../db/record-id'
2
+ import { buildAgentPromptContext } from './agent-prompt-context'
3
+ import { buildIndexedRepositoriesContext, getPluginService } from './plugin-resolution'
4
+ import type {
5
+ LotaRuntimeIndexedRepositoriesContext,
6
+ LotaRuntimeTurnHooks,
7
+ LotaRuntimeWorkspaceLifecycleState,
8
+ LotaRuntimeWorkspaceProjectionState,
9
+ LotaRuntimeWorkspaceProvider,
10
+ } from './runtime-extensions'
11
+ import { asRecord, readInstructionSections, readOptionalString } from './workstream-chat-helpers'
12
+
13
+ function readOptionalBoolean(value: unknown): boolean | undefined {
14
+ return typeof value === 'boolean' ? value : undefined
15
+ }
16
+
17
+ interface AssembledWorkstreamTurnContext {
18
+ workspace: Record<string, unknown>
19
+ workspaceLifecycleState: LotaRuntimeWorkspaceLifecycleState | undefined
20
+ workspaceProfileState: LotaRuntimeWorkspaceProjectionState | undefined
21
+ onboardingActive: boolean
22
+ linearInstalled: boolean
23
+ githubInstalled: boolean
24
+ indexedRepoContext: LotaRuntimeIndexedRepositoriesContext
25
+ promptContext: { systemWorkspaceDetails?: string }
26
+ retrievedKnowledgeSection: string | undefined
27
+ buildContextResult: Record<string, unknown> | null
28
+ hookInstructionSections: string[]
29
+ }
30
+
31
+ export async function assembleWorkstreamTurnContext(params: {
32
+ workstream: unknown
33
+ workstreamRef: RecordIdRef
34
+ orgRef: RecordIdRef
35
+ userRef: RecordIdRef
36
+ userName?: string | null
37
+ orgIdString: string
38
+ userIdString: string
39
+ messageText: string
40
+ workspacePromise: Promise<Record<string, unknown>>
41
+ workspaceProvider?: LotaRuntimeWorkspaceProvider
42
+ turnHooks: LotaRuntimeTurnHooks
43
+ onStep?: (name: string) => void
44
+ }): Promise<AssembledWorkstreamTurnContext> {
45
+ const workspace = await params.workspacePromise
46
+ params.onStep?.('fetch-workspace')
47
+
48
+ const [workspaceLifecycleState, workspaceProfileState] = await Promise.all([
49
+ params.workspaceProvider ? params.workspaceProvider.getLifecycleState?.(workspace) : Promise.resolve(undefined),
50
+ params.workspaceProvider
51
+ ? params.workspaceProvider.readProfileProjectionState?.(workspace)
52
+ : Promise.resolve(undefined),
53
+ ])
54
+ params.onStep?.('workspace-state')
55
+
56
+ const onboardingActive = workspaceLifecycleState?.bootstrapActive ?? false
57
+ const getLinearInstallationByOrgId = getPluginService([
58
+ 'linear',
59
+ 'services',
60
+ 'linearService',
61
+ 'getInstallationByOrgId',
62
+ ])
63
+ const getGithubInstallationForOrganization = getPluginService([
64
+ 'github',
65
+ 'services',
66
+ 'githubService',
67
+ 'getInstallationForOrganization',
68
+ ])
69
+ const [linearInstallation, githubInstallation, indexedRepoContext, recentDomainEvents, promptSummary] =
70
+ await Promise.all([
71
+ getLinearInstallationByOrgId
72
+ ? (getLinearInstallationByOrgId(params.orgRef) as Promise<unknown>)
73
+ : Promise.resolve(null),
74
+ getGithubInstallationForOrganization
75
+ ? (getGithubInstallationForOrganization(params.orgIdString) as Promise<unknown>)
76
+ : Promise.resolve(null),
77
+ buildIndexedRepositoriesContext(params.orgIdString),
78
+ params.workspaceProvider?.listRecentDomainEvents?.(params.orgRef, 5) ??
79
+ Promise.resolve([] as Array<Record<string, unknown>>),
80
+ params.workspaceProvider?.buildPromptSummary
81
+ ? params.workspaceProvider.buildPromptSummary(params.orgRef).catch(() => undefined)
82
+ : Promise.resolve(undefined),
83
+ ])
84
+ params.onStep?.('parallel-context-fetch(plugins+repos+events+summary)')
85
+
86
+ let linearInstalled = Boolean(linearInstallation)
87
+ let githubInstalled = Boolean(githubInstallation)
88
+ let promptContext = buildAgentPromptContext({
89
+ workspaceName:
90
+ workspaceProfileState?.workspaceName ?? readOptionalString((workspace as { name?: unknown }).name) ?? undefined,
91
+ summaryBlock: workspaceProfileState?.summaryBlock,
92
+ promptSummary,
93
+ userName: params.userName ?? undefined,
94
+ recentDomainEvents,
95
+ })
96
+ let retrievedKnowledgeSection: string | undefined =
97
+ onboardingActive || !params.messageText
98
+ ? undefined
99
+ : await params.workspaceProvider?.buildRetrievedKnowledgeSection?.({
100
+ workspaceId: params.orgIdString,
101
+ userId: params.userIdString,
102
+ query: params.messageText,
103
+ })
104
+ params.onStep?.('rag-knowledge-retrieval')
105
+
106
+ const buildContextResult = asRecord(
107
+ await params.turnHooks.buildContext?.({
108
+ workstream: params.workstream,
109
+ workstreamRef: params.workstreamRef,
110
+ orgRef: params.orgRef,
111
+ userRef: params.userRef,
112
+ userName: params.userName,
113
+ workspace,
114
+ onboardingActive,
115
+ messageText: params.messageText,
116
+ linearInstalled,
117
+ githubInstalled,
118
+ indexedRepoContext,
119
+ promptContext,
120
+ workspaceLifecycleState,
121
+ workspaceProfileState,
122
+ promptSummary,
123
+ recentDomainEvents,
124
+ retrievedKnowledgeSection,
125
+ }),
126
+ )
127
+ params.onStep?.('hook:buildContext')
128
+
129
+ const buildContextPromptDetails = readOptionalString(buildContextResult?.systemWorkspaceDetails)
130
+ if (buildContextPromptDetails) {
131
+ promptContext = { systemWorkspaceDetails: buildContextPromptDetails }
132
+ }
133
+ const buildContextRetrievedKnowledge = readOptionalString(buildContextResult?.retrievedKnowledgeSection)
134
+ if (buildContextRetrievedKnowledge !== undefined) {
135
+ retrievedKnowledgeSection = buildContextRetrievedKnowledge
136
+ }
137
+ const buildContextLinearInstalled = readOptionalBoolean(buildContextResult?.linearInstalled)
138
+ if (buildContextLinearInstalled !== undefined) {
139
+ linearInstalled = buildContextLinearInstalled
140
+ }
141
+ const buildContextGithubInstalled = readOptionalBoolean(buildContextResult?.githubInstalled)
142
+ if (buildContextGithubInstalled !== undefined) {
143
+ githubInstalled = buildContextGithubInstalled
144
+ }
145
+
146
+ const hookInstructionSections = readInstructionSections(
147
+ await params.turnHooks.buildExtraInstructionSections?.({
148
+ workstream: params.workstream,
149
+ workstreamRef: params.workstreamRef,
150
+ orgRef: params.orgRef,
151
+ userRef: params.userRef,
152
+ userName: params.userName,
153
+ workspace,
154
+ onboardingActive,
155
+ messageText: params.messageText,
156
+ linearInstalled,
157
+ githubInstalled,
158
+ indexedRepoContext,
159
+ promptContext,
160
+ workspaceLifecycleState,
161
+ workspaceProfileState,
162
+ promptSummary,
163
+ recentDomainEvents,
164
+ retrievedKnowledgeSection,
165
+ context: buildContextResult,
166
+ }),
167
+ )
168
+ params.onStep?.('hook:buildExtraInstructionSections')
169
+
170
+ return {
171
+ workspace,
172
+ workspaceLifecycleState,
173
+ workspaceProfileState,
174
+ onboardingActive,
175
+ linearInstalled,
176
+ githubInstalled,
177
+ indexedRepoContext,
178
+ promptContext,
179
+ retrievedKnowledgeSection,
180
+ buildContextResult,
181
+ hookInstructionSections,
182
+ }
183
+ }
@@ -31,6 +31,7 @@ import { databaseService } from '../db/service'
31
31
  import { TABLES } from '../db/tables'
32
32
  import type { AutonomousJobQueuePayload } from '../queues/autonomous-job.queue'
33
33
  import { extractMessageText } from '../runtime/workstream-chat-helpers'
34
+ import { buildAutonomousAtJobId, encodeBullmqId } from '../utils/autonomous-job-ids'
34
35
  import { toIsoDateTimeString, toOptionalIsoDateTimeString } from '../utils/date-time'
35
36
  import { compactRecord, compactWhitespace, stringifyUnknown, truncateText } from '../utils/string'
36
37
  import { executionPlanService } from './execution-plan.service'
@@ -41,14 +42,6 @@ import { workstreamService } from './workstream.service'
41
42
 
42
43
  const AUTONOMOUS_JOB_QUEUE_NAME = 'autonomous-job'
43
44
 
44
- function encodeBullmqId(raw: string): string {
45
- return Buffer.from(raw).toString('base64url')
46
- }
47
-
48
- function buildAutonomousAtJobId(autonomousJobId: string): string {
49
- return `autonomous-at-${encodeBullmqId(autonomousJobId)}`
50
- }
51
-
52
45
  function buildAutonomousManualJobId(autonomousJobId: string): string {
53
46
  return `autonomous-manual-${encodeBullmqId(autonomousJobId)}-${Date.now()}`
54
47
  }