@lota-sdk/core 0.1.15 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/infrastructure/schema/00_identity.surql +0 -2
- package/infrastructure/schema/01_memory.surql +1 -1
- package/infrastructure/schema/02_execution_plan.surql +62 -1
- package/infrastructure/schema/03_learned_skill.surql +1 -1
- package/infrastructure/schema/06_playbook.surql +25 -0
- package/infrastructure/schema/07_institutional_memory.surql +13 -0
- package/infrastructure/schema/08_quality_metrics.surql +17 -0
- package/package.json +8 -7
- package/src/ai/definitions.ts +80 -2
- package/src/ai/index.ts +0 -2
- package/src/bifrost/bifrost.ts +2 -7
- package/src/config/agent-defaults.ts +31 -21
- package/src/config/agent-types.ts +11 -0
- package/src/config/constants.ts +2 -14
- package/src/config/debug-logger.ts +5 -1
- package/src/config/index.ts +3 -0
- package/src/config/model-constants.ts +16 -34
- package/src/config/search.ts +1 -15
- package/src/create-runtime.ts +244 -178
- package/src/db/cursor-pagination.ts +3 -6
- package/src/db/index.ts +2 -0
- package/src/db/memory-store.rows.ts +7 -7
- package/src/db/memory-store.ts +14 -18
- package/src/db/memory.ts +13 -13
- package/src/db/service.ts +153 -79
- package/src/db/startup.ts +6 -10
- package/src/db/surreal-mutation.ts +43 -0
- package/src/db/tables.ts +7 -0
- package/src/db/workstream-message-row.ts +15 -0
- package/src/embeddings/provider.ts +1 -1
- package/src/queues/context-compaction.queue.ts +15 -46
- package/src/queues/delayed-node-promotion.queue.ts +41 -0
- package/src/queues/index.ts +3 -0
- package/src/queues/memory-consolidation.queue.ts +16 -51
- package/src/queues/plan-scheduler.queue.ts +97 -0
- package/src/queues/post-chat-memory.queue.ts +15 -56
- package/src/queues/queue-factory.ts +100 -0
- package/src/queues/recent-activity-title-refinement.queue.ts +15 -50
- package/src/queues/regular-chat-memory-digest.queue.ts +16 -52
- package/src/queues/skill-extraction.queue.ts +15 -47
- package/src/queues/workstream-title-generation.queue.ts +15 -47
- package/src/redis/connection.ts +6 -0
- package/src/redis/index.ts +1 -1
- package/src/redis/stream-context.ts +11 -0
- package/src/runtime/agent-runtime-policy.ts +106 -21
- package/src/runtime/approval-continuation.ts +12 -6
- package/src/runtime/context-compaction-runtime.ts +1 -1
- package/src/runtime/context-compaction.ts +22 -60
- package/src/runtime/execution-plan.ts +22 -18
- package/src/runtime/graph-designer.ts +15 -0
- package/src/runtime/helper-model.ts +9 -197
- package/src/runtime/index.ts +2 -0
- package/src/runtime/llm-content.ts +1 -1
- package/src/runtime/memory-block.ts +9 -11
- package/src/runtime/memory-pipeline.ts +6 -9
- package/src/runtime/plugin-resolution.ts +35 -0
- package/src/runtime/plugin-types.ts +72 -0
- package/src/runtime/retrieval-adapters.ts +1 -1
- package/src/runtime/runtime-config.ts +25 -12
- package/src/runtime/runtime-extensions.ts +2 -2
- package/src/runtime/runtime-worker-registry.ts +6 -0
- package/src/runtime/team-consultation-orchestrator.ts +45 -28
- package/src/runtime/team-consultation-prompts.ts +11 -2
- package/src/runtime/title-helpers.ts +2 -4
- package/src/runtime/workstream-chat-helpers.ts +1 -1
- package/src/services/adaptive-playbook.service.ts +152 -0
- package/src/services/agent-executor.service.ts +293 -0
- package/src/services/artifact-provenance.service.ts +172 -0
- package/src/services/attachment.service.ts +6 -11
- package/src/services/context-compaction.service.ts +72 -55
- package/src/services/context-enrichment.service.ts +33 -0
- package/src/services/coordination-registry.service.ts +117 -0
- package/src/services/document-chunk.service.ts +1 -1
- package/src/services/domain-agent-executor.service.ts +71 -0
- package/src/services/execution-plan.service.ts +269 -50
- package/src/services/feedback-loop.service.ts +96 -0
- package/src/services/global-orchestrator.service.ts +148 -0
- package/src/services/index.ts +26 -0
- package/src/services/institutional-memory.service.ts +145 -0
- package/src/services/learned-skill.service.ts +24 -5
- package/src/services/memory-assessment.service.ts +3 -2
- package/src/services/memory-utils.ts +3 -8
- package/src/services/memory.service.ts +42 -59
- package/src/services/monitoring-window.service.ts +86 -0
- package/src/services/mutating-approval.service.ts +1 -1
- package/src/services/node-workspace.service.ts +155 -0
- package/src/services/notification.service.ts +39 -0
- package/src/services/organization-member.service.ts +11 -4
- package/src/services/organization.service.ts +5 -5
- package/src/services/ownership-dispatcher.service.ts +403 -0
- package/src/services/plan-approval.service.ts +1 -1
- package/src/services/plan-builder.service.ts +1 -0
- package/src/services/plan-checkpoint.service.ts +30 -2
- package/src/services/plan-compiler.service.ts +5 -0
- package/src/services/plan-coordination.service.ts +152 -0
- package/src/services/plan-cycle.service.ts +284 -0
- package/src/services/plan-deadline.service.ts +287 -0
- package/src/services/plan-executor.service.ts +384 -40
- package/src/services/plan-run.service.ts +41 -7
- package/src/services/plan-scheduler.service.ts +240 -0
- package/src/services/plan-template.service.ts +117 -0
- package/src/services/plan-validator.service.ts +84 -2
- package/src/services/plan-workspace.service.ts +83 -0
- package/src/services/playbook-registry.service.ts +67 -0
- package/src/services/plugin-executor.service.ts +103 -0
- package/src/services/quality-metrics.service.ts +132 -0
- package/src/services/recent-activity.service.ts +27 -31
- package/src/services/skill-resolver.service.ts +19 -0
- package/src/services/system-executor.service.ts +105 -0
- package/src/services/workstream-message.service.ts +12 -34
- package/src/services/workstream-plan-registry.service.ts +22 -0
- package/src/services/workstream-title.service.ts +3 -1
- package/src/services/workstream-turn-preparation.service.ts +34 -66
- package/src/services/workstream.service.ts +33 -55
- package/src/services/workstream.types.ts +9 -9
- package/src/services/write-intent-validator.service.ts +81 -0
- package/src/storage/attachment-parser.ts +1 -1
- package/src/storage/attachment-utils.ts +1 -1
- package/src/storage/generated-document-storage.service.ts +3 -2
- package/src/system-agents/delegated-agent-factory.ts +2 -0
- package/src/tools/execution-plan.tool.ts +17 -23
- package/src/tools/index.ts +0 -1
- package/src/tools/team-think.tool.ts +6 -4
- package/src/utils/async.ts +2 -1
- package/src/utils/date-time.ts +4 -32
- package/src/utils/env.ts +8 -0
- package/src/utils/errors.ts +42 -10
- package/src/utils/index.ts +9 -0
- package/src/utils/string.ts +114 -1
- package/src/workers/index.ts +1 -0
- package/src/workers/regular-chat-memory-digest.runner.ts +2 -2
- package/src/workers/skill-extraction.runner.ts +1 -1
- package/src/workers/utils/file-section-chunker.ts +2 -1
- package/src/workers/utils/repomix-file-sections.ts +2 -2
- package/src/workers/utils/sandbox-error.ts +11 -2
- package/src/workers/utils/workstream-message-query.ts +11 -20
- package/src/workers/worker-utils.ts +2 -2
- package/src/tools/log-hello-world.tool.ts +0 -17
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
|
|
3
3
|
import type { CoreWorkstreamProfile } from '../config/agent-defaults'
|
|
4
|
+
import type { AgentFactory, AgentRuntimeConfigProvider, AgentToolBuilder } from '../config/agent-types'
|
|
4
5
|
import type { LotaWorkstreamConfig, WorkstreamBootstrapWelcomeConfig } from '../config/workstream-defaults'
|
|
5
|
-
import type {
|
|
6
|
+
import type { NotificationService } from '../services/notification.service'
|
|
7
|
+
import { isRecord } from '../utils/string'
|
|
8
|
+
import type { GraphDesigner } from './graph-designer'
|
|
9
|
+
import type { LotaPlugin, SystemNodeExecutor } from './plugin-types'
|
|
6
10
|
import type { LotaRuntimeAdapters, LotaRuntimeTurnHooks } from './runtime-extensions'
|
|
7
11
|
import type { LotaRuntimeWorkerExtensions } from './runtime-worker-registry'
|
|
8
12
|
|
|
9
13
|
const logLevelValues = ['trace', 'debug', 'info', 'warning', 'error', 'fatal'] as const
|
|
10
14
|
|
|
11
|
-
type LotaAgentFactoryRegistry = Record<string, (...args: unknown[]) => unknown>
|
|
12
|
-
|
|
13
|
-
const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null
|
|
14
|
-
|
|
15
15
|
function isStringOrUrl(value: unknown): value is string | URL {
|
|
16
16
|
return typeof value === 'string' || value instanceof URL
|
|
17
17
|
}
|
|
@@ -24,7 +24,7 @@ function isStringRecord(value: unknown): value is Record<string, string> {
|
|
|
24
24
|
return isRecord(value) && Object.values(value).every((entry) => typeof entry === 'string')
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
function isAgentFactoryRegistry(value: unknown): value is
|
|
27
|
+
function isAgentFactoryRegistry(value: unknown): value is AgentFactory {
|
|
28
28
|
return isRecord(value) && Object.values(value).every((entry) => typeof entry === 'function')
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -32,10 +32,22 @@ function isPluginRuntimeRecord(value: unknown): value is Record<string, LotaPlug
|
|
|
32
32
|
return isRecord(value)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
function isSystemExecutorRecord(value: unknown): value is Record<string, SystemNodeExecutor> {
|
|
36
|
+
return isRecord(value)
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
function isToolProviderRecord(value: unknown): value is Record<string, unknown> {
|
|
36
40
|
return isRecord(value)
|
|
37
41
|
}
|
|
38
42
|
|
|
43
|
+
function isNotificationService(value: unknown): value is NotificationService {
|
|
44
|
+
return isRecord(value) && isFunction(value.notify) && isFunction(value.remind) && isFunction(value.escalate)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isGraphDesigner(value: unknown): value is GraphDesigner {
|
|
48
|
+
return isRecord(value) && isFunction(value.designGraph)
|
|
49
|
+
}
|
|
50
|
+
|
|
39
51
|
function isWorkerExtensionRecord(value: unknown): value is LotaRuntimeWorkerExtensions {
|
|
40
52
|
if (!isRecord(value)) return false
|
|
41
53
|
|
|
@@ -85,15 +97,13 @@ const agentsConfigSchema = z
|
|
|
85
97
|
})
|
|
86
98
|
.optional(),
|
|
87
99
|
createAgent: z
|
|
88
|
-
.custom<
|
|
89
|
-
error: 'agents.createAgent must be a function registry',
|
|
90
|
-
})
|
|
100
|
+
.custom<AgentFactory>(isAgentFactoryRegistry, { error: 'agents.createAgent must be a function registry' })
|
|
91
101
|
.optional(),
|
|
92
102
|
buildAgentTools: z
|
|
93
|
-
.custom<
|
|
103
|
+
.custom<AgentToolBuilder>(isFunction, { error: 'agents.buildAgentTools must be a function' })
|
|
94
104
|
.optional(),
|
|
95
105
|
getAgentRuntimeConfig: z
|
|
96
|
-
.custom<
|
|
106
|
+
.custom<AgentRuntimeConfigProvider>(isFunction, { error: 'agents.getAgentRuntimeConfig must be a function' })
|
|
97
107
|
.optional(),
|
|
98
108
|
})
|
|
99
109
|
.superRefine((value, ctx) => {
|
|
@@ -173,8 +183,11 @@ export const LotaRuntimeConfigSchema = z.object({
|
|
|
173
183
|
extraSchemaFiles: z.array(z.custom<string | URL>(isStringOrUrl)).optional(),
|
|
174
184
|
extraWorkers: z.custom<LotaRuntimeWorkerExtensions>(isWorkerExtensionRecord).optional(),
|
|
175
185
|
pluginRuntime: z.custom<Record<string, LotaPlugin>>(isPluginRuntimeRecord).optional(),
|
|
186
|
+
systemExecutors: z.custom<Record<string, SystemNodeExecutor>>(isSystemExecutorRecord).optional(),
|
|
187
|
+
notificationService: z.custom<NotificationService>(isNotificationService).optional(),
|
|
176
188
|
runtimeAdapters: z.custom<LotaRuntimeAdapters>(isRecord).optional(),
|
|
177
189
|
turnHooks: z.custom<LotaRuntimeTurnHooks>(isRecord).optional(),
|
|
190
|
+
graphDesigner: z.custom<GraphDesigner>(isGraphDesigner).optional(),
|
|
178
191
|
})
|
|
179
192
|
|
|
180
193
|
export type LotaRuntimeConfig = z.input<typeof LotaRuntimeConfigSchema>
|
|
@@ -235,4 +248,4 @@ export function parseWorkerBootstrapEnv(env: Record<string, string | undefined>)
|
|
|
235
248
|
return WORKER_BOOTSTRAP_ENV_SCHEMA.parse(env)
|
|
236
249
|
}
|
|
237
250
|
|
|
238
|
-
export type {
|
|
251
|
+
export type { LotaWorkstreamConfig }
|
|
@@ -224,8 +224,8 @@ export function getConfiguredPluginDatabaseConnector(): (() => Promise<void>) |
|
|
|
224
224
|
export async function withConfiguredWorkspaceMemoryLock<T>(workspaceId: string, fn: () => Promise<T>): Promise<T> {
|
|
225
225
|
const adapter = runtimeExtensionsState.adapters.workers?.withWorkspaceMemoryLock
|
|
226
226
|
if (!adapter) {
|
|
227
|
-
return
|
|
227
|
+
return fn()
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
return
|
|
230
|
+
return adapter(workspaceId, fn)
|
|
231
231
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { startContextCompactionWorker } from '../queues/context-compaction.queue'
|
|
2
|
+
import { startDelayedNodePromotionWorker } from '../queues/delayed-node-promotion.queue'
|
|
2
3
|
import { scheduleRecurringConsolidation, startMemoryConsolidationWorker } from '../queues/memory-consolidation.queue'
|
|
4
|
+
import { startPlanSchedulerWorker } from '../queues/plan-scheduler.queue'
|
|
3
5
|
import { startPostChatMemoryWorker } from '../queues/post-chat-memory.queue'
|
|
4
6
|
import { startRecentActivityTitleRefinementWorker } from '../queues/recent-activity-title-refinement.queue'
|
|
5
7
|
import { startRegularChatMemoryDigestWorker } from '../queues/regular-chat-memory-digest.queue'
|
|
@@ -8,7 +10,9 @@ import { startWorkstreamTitleGenerationWorker } from '../queues/workstream-title
|
|
|
8
10
|
|
|
9
11
|
export interface LotaRuntimeWorkerStartRegistry {
|
|
10
12
|
contextCompaction: typeof startContextCompactionWorker
|
|
13
|
+
delayedNodePromotion: typeof startDelayedNodePromotionWorker
|
|
11
14
|
memoryConsolidation: typeof startMemoryConsolidationWorker
|
|
15
|
+
planScheduler: typeof startPlanSchedulerWorker
|
|
12
16
|
postChatMemory: typeof startPostChatMemoryWorker
|
|
13
17
|
regularChatMemoryDigest: typeof startRegularChatMemoryDigestWorker
|
|
14
18
|
skillExtraction: typeof startSkillExtractionWorker
|
|
@@ -34,7 +38,9 @@ export function buildRuntimeWorkerRegistry(extraWorkers?: LotaRuntimeWorkerExten
|
|
|
34
38
|
return {
|
|
35
39
|
start: {
|
|
36
40
|
contextCompaction: startContextCompactionWorker,
|
|
41
|
+
delayedNodePromotion: startDelayedNodePromotionWorker,
|
|
37
42
|
memoryConsolidation: startMemoryConsolidationWorker,
|
|
43
|
+
planScheduler: startPlanSchedulerWorker,
|
|
38
44
|
postChatMemory: startPostChatMemoryWorker,
|
|
39
45
|
regularChatMemoryDigest: startRegularChatMemoryDigestWorker,
|
|
40
46
|
skillExtraction: startSkillExtractionWorker,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ConsultTeamArgsSchema, withMessageCreatedAt } from '@lota-sdk/shared'
|
|
2
2
|
import type { ChatMessage, ConsultTeamResultData } from '@lota-sdk/shared'
|
|
3
|
-
import { convertToModelMessages,
|
|
3
|
+
import { convertToModelMessages, tool as createTool } from 'ai'
|
|
4
4
|
|
|
5
5
|
import { agentDisplayNames, teamConsultParticipants } from '../config/agent-defaults'
|
|
6
6
|
import { createTimedAbortSignal } from './agent-stream-helpers'
|
|
@@ -33,6 +33,28 @@ function getConsultTeamOutput(output: unknown): ConsultTeamResultData | undefine
|
|
|
33
33
|
return undefined
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
function findLastUserMessage(
|
|
37
|
+
messages: ChatMessage[],
|
|
38
|
+
predicate: (message: ChatMessage) => boolean,
|
|
39
|
+
): ChatMessage | null {
|
|
40
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
41
|
+
const message = messages[index]
|
|
42
|
+
if (predicate(message)) {
|
|
43
|
+
return message
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function selectTeamConsultationContextMessages(messages: ChatMessage[], latestUserMessageId: string): ChatMessage[] {
|
|
51
|
+
const latestUserMessage =
|
|
52
|
+
findLastUserMessage(messages, (message) => message.id === latestUserMessageId && message.role === 'user') ??
|
|
53
|
+
findLastUserMessage(messages, (message) => message.role === 'user')
|
|
54
|
+
|
|
55
|
+
return latestUserMessage ? [latestUserMessage] : []
|
|
56
|
+
}
|
|
57
|
+
|
|
36
58
|
interface ParticipantObserver {
|
|
37
59
|
run<T>(fn: () => T | Promise<T>): Promise<T>
|
|
38
60
|
recordError?: (error: unknown) => void
|
|
@@ -51,6 +73,7 @@ export interface TeamConsultationParticipantRunner {
|
|
|
51
73
|
},
|
|
52
74
|
): Promise<{
|
|
53
75
|
agent: {
|
|
76
|
+
generate(params: Record<string, unknown>): Promise<{ text: string }>
|
|
54
77
|
stream(
|
|
55
78
|
params: Record<string, unknown>,
|
|
56
79
|
): Promise<{
|
|
@@ -119,16 +142,22 @@ export function createConsultTeamTool(params: CreateConsultTeamToolParams) {
|
|
|
119
142
|
})
|
|
120
143
|
const modelMessages = await convertToModelMessages(
|
|
121
144
|
buildModelInputMessagesWithUploadMetadata({
|
|
122
|
-
messages: params.historyMessages,
|
|
145
|
+
messages: selectTeamConsultationContextMessages(params.historyMessages, params.latestUserMessageId),
|
|
123
146
|
latestUserMessageId: params.latestUserMessageId,
|
|
124
147
|
uploadMetadataText,
|
|
125
148
|
}),
|
|
126
149
|
{ ignoreIncompleteToolCalls: true },
|
|
127
150
|
)
|
|
128
151
|
|
|
129
|
-
let result: Awaited<ReturnType<typeof agent.
|
|
152
|
+
let result: Awaited<ReturnType<typeof agent.generate>>
|
|
130
153
|
try {
|
|
131
|
-
result = await observer.run(() =>
|
|
154
|
+
result = await observer.run(() =>
|
|
155
|
+
agent.generate({
|
|
156
|
+
messages: modelMessages,
|
|
157
|
+
abortSignal: timedAbort.signal,
|
|
158
|
+
timeout: TEAM_CONSULTATION_TIMEOUT_MS,
|
|
159
|
+
}),
|
|
160
|
+
)
|
|
132
161
|
} catch (error) {
|
|
133
162
|
if (params.abortSignal.aborted || timedAbort.signal.aborted) {
|
|
134
163
|
observer.recordAbort?.(error)
|
|
@@ -138,33 +167,21 @@ export function createConsultTeamTool(params: CreateConsultTeamToolParams) {
|
|
|
138
167
|
throw error
|
|
139
168
|
}
|
|
140
169
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
generateMessageId: () => Bun.randomUUIDv7(),
|
|
144
|
-
sendReasoning: true,
|
|
145
|
-
sendSources: true,
|
|
146
|
-
sendStart: false,
|
|
147
|
-
sendFinish: false,
|
|
148
|
-
}) as ReadableStream<never>,
|
|
149
|
-
onError: (error) => {
|
|
150
|
-
params.onReadError?.(agentId, error)
|
|
151
|
-
},
|
|
152
|
-
})) {
|
|
153
|
-
latestMessage = withMessageCreatedAt(message)
|
|
154
|
-
responses[index] = {
|
|
155
|
-
agentId,
|
|
156
|
-
agentName,
|
|
157
|
-
status: 'running',
|
|
158
|
-
summary: extractMessageText(latestMessage).trim() || undefined,
|
|
159
|
-
message: latestMessage,
|
|
160
|
-
}
|
|
161
|
-
pushSnapshot()
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (!latestMessage) {
|
|
170
|
+
const responseText = result.text.trim()
|
|
171
|
+
if (!responseText) {
|
|
165
172
|
throw new Error(`Team participant ${agentId} did not produce a response.`)
|
|
166
173
|
}
|
|
167
174
|
|
|
175
|
+
latestMessage = withMessageCreatedAt(
|
|
176
|
+
{
|
|
177
|
+
id: Bun.randomUUIDv7(),
|
|
178
|
+
role: 'assistant',
|
|
179
|
+
parts: [{ type: 'text', text: responseText }],
|
|
180
|
+
metadata: { agentId, agentName },
|
|
181
|
+
} satisfies ChatMessage,
|
|
182
|
+
Date.now(),
|
|
183
|
+
)
|
|
184
|
+
|
|
168
185
|
responses[index] = {
|
|
169
186
|
agentId,
|
|
170
187
|
agentName,
|
|
@@ -3,12 +3,21 @@ import { agentDisplayNames, getLeadAgentDisplayName } from '../config/agent-defa
|
|
|
3
3
|
export function buildTeamConsultationResponseGuard(params: { agentId: string; task: string }) {
|
|
4
4
|
const agentName = agentDisplayNames[params.agentId] ?? params.agentId
|
|
5
5
|
const leadAgentDisplayName = getLeadAgentDisplayName()
|
|
6
|
+
const mentorConstraint =
|
|
7
|
+
params.agentId === 'mentor'
|
|
8
|
+
? ['- As Mentor, answer as an experienced operator reviewing launch discipline, not as a coach or therapist.']
|
|
9
|
+
: []
|
|
6
10
|
return [
|
|
7
11
|
'<team-consultation-agent-protocol>',
|
|
8
12
|
`- You are participating in a structured internal team consultation led by ${leadAgentDisplayName}.`,
|
|
9
13
|
`- Your role for this response is ${agentName}.`,
|
|
10
|
-
'-
|
|
11
|
-
'-
|
|
14
|
+
'- Chief is already coordinating this panel. Do not refer the task back to Chief or tell the user to consult another agent.',
|
|
15
|
+
'- Answer only from your role-specific perspective for the task below.',
|
|
16
|
+
'- Do not coach the user, ask follow-up questions, or challenge assumptions in this panel response. Give the recommendation directly.',
|
|
17
|
+
...mentorConstraint,
|
|
18
|
+
'- Return exactly 3 short markdown bullets in this order: recommendation, key risk/tradeoff, next decision.',
|
|
19
|
+
'- Keep the whole response under 120 words.',
|
|
20
|
+
'- Do not use headings, code fences, XML, evidence blocks, gap lists, or preamble text.',
|
|
12
21
|
'',
|
|
13
22
|
'<team-consultation-task>',
|
|
14
23
|
params.task.trim(),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { compactWhitespace } from '../utils/string'
|
|
1
|
+
import { compactWhitespace, truncateText } from '../utils/string'
|
|
2
2
|
|
|
3
3
|
const TITLE_WORD_LIMIT = 5
|
|
4
4
|
|
|
@@ -8,9 +8,7 @@ export function limitTitleWords(text: string): string {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export function deriveTitle(text: string): string {
|
|
11
|
-
|
|
12
|
-
if (trimmed.length <= 60) return trimmed
|
|
13
|
-
return `${trimmed.slice(0, 57)}...`
|
|
11
|
+
return truncateText(compactWhitespace(text), 60)
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
export function normalizeTitle(value: string): string {
|
|
@@ -77,7 +77,7 @@ export function appendPersistedWorkstreamContextToHistoryMessages(
|
|
|
77
77
|
nextHistoryMessages.push({ role: 'agent', content: `Compacted chat summary:\n${compactionSummary}` })
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
if (params.persistedState !==
|
|
80
|
+
if (params.persistedState !== null && params.persistedState !== undefined) {
|
|
81
81
|
nextHistoryMessages.push({
|
|
82
82
|
role: 'agent',
|
|
83
83
|
content: `Structured workstream state:\n${JSON.stringify(params.persistedState)}`,
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { PlaybookVersion, Recommendation } from '@lota-sdk/shared'
|
|
2
|
+
import { PlaybookSchema, PlaybookVersionSchema } from '@lota-sdk/shared'
|
|
3
|
+
|
|
4
|
+
import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
5
|
+
import { databaseService } from '../db/service'
|
|
6
|
+
import { TABLES } from '../db/tables'
|
|
7
|
+
|
|
8
|
+
class AdaptivePlaybookService {
|
|
9
|
+
async refineFromCycle(params: {
|
|
10
|
+
playbookId: string
|
|
11
|
+
runId: string
|
|
12
|
+
recommendations: Recommendation[]
|
|
13
|
+
organizationId: string
|
|
14
|
+
}): Promise<PlaybookVersion> {
|
|
15
|
+
const playbook = await databaseService.findOne(
|
|
16
|
+
TABLES.PLAYBOOK,
|
|
17
|
+
{ id: ensureRecordId(params.playbookId, TABLES.PLAYBOOK) },
|
|
18
|
+
PlaybookSchema,
|
|
19
|
+
)
|
|
20
|
+
if (!playbook) {
|
|
21
|
+
throw new Error(`Playbook not found: ${params.playbookId}`)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const currentVersion = await databaseService.findOne(
|
|
25
|
+
TABLES.PLAYBOOK_VERSION,
|
|
26
|
+
{
|
|
27
|
+
id: ensureRecordId(
|
|
28
|
+
recordIdToString(playbook.currentVersionId, TABLES.PLAYBOOK_VERSION),
|
|
29
|
+
TABLES.PLAYBOOK_VERSION,
|
|
30
|
+
),
|
|
31
|
+
},
|
|
32
|
+
PlaybookVersionSchema,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
const nextVersionNumber = currentVersion ? currentVersion.version + 1 : 1
|
|
36
|
+
const now = new Date()
|
|
37
|
+
|
|
38
|
+
const newVersion = await databaseService.create(
|
|
39
|
+
TABLES.PLAYBOOK_VERSION,
|
|
40
|
+
{
|
|
41
|
+
playbookId: ensureRecordId(params.playbookId, TABLES.PLAYBOOK),
|
|
42
|
+
version: nextVersionNumber,
|
|
43
|
+
parentVersionId: playbook.currentVersionId,
|
|
44
|
+
appliedRecommendations: params.recommendations.map((r) => r.description),
|
|
45
|
+
status: 'testing',
|
|
46
|
+
createdAt: now,
|
|
47
|
+
},
|
|
48
|
+
PlaybookVersionSchema,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
await databaseService.update(
|
|
52
|
+
TABLES.PLAYBOOK,
|
|
53
|
+
ensureRecordId(params.playbookId, TABLES.PLAYBOOK),
|
|
54
|
+
{
|
|
55
|
+
currentVersionId: newVersion.id,
|
|
56
|
+
previousVersionId: playbook.currentVersionId,
|
|
57
|
+
cycleCount: playbook.cycleCount + 1,
|
|
58
|
+
},
|
|
59
|
+
PlaybookSchema,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return newVersion
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
evaluateRegression(params: { currentScore: number; previousScore: number; threshold?: number }): {
|
|
66
|
+
shouldRollback: boolean
|
|
67
|
+
} {
|
|
68
|
+
const threshold = params.threshold ?? 0.9
|
|
69
|
+
if (params.previousScore === 0) return { shouldRollback: false }
|
|
70
|
+
return { shouldRollback: params.currentScore < params.previousScore * threshold }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async rollback(params: {
|
|
74
|
+
playbookId: string
|
|
75
|
+
organizationId: string
|
|
76
|
+
maxLevels?: number
|
|
77
|
+
}): Promise<PlaybookVersion | null> {
|
|
78
|
+
const maxLevels = params.maxLevels ?? 3
|
|
79
|
+
return this.rollbackRecursive(params.playbookId, params.organizationId, maxLevels)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private async rollbackRecursive(
|
|
83
|
+
playbookId: string,
|
|
84
|
+
organizationId: string,
|
|
85
|
+
remainingLevels: number,
|
|
86
|
+
): Promise<PlaybookVersion | null> {
|
|
87
|
+
if (remainingLevels <= 0) return null
|
|
88
|
+
|
|
89
|
+
const playbook = await databaseService.findOne(
|
|
90
|
+
TABLES.PLAYBOOK,
|
|
91
|
+
{ id: ensureRecordId(playbookId, TABLES.PLAYBOOK) },
|
|
92
|
+
PlaybookSchema,
|
|
93
|
+
)
|
|
94
|
+
if (!playbook) return null
|
|
95
|
+
|
|
96
|
+
const currentVersion = await databaseService.findOne(
|
|
97
|
+
TABLES.PLAYBOOK_VERSION,
|
|
98
|
+
{
|
|
99
|
+
id: ensureRecordId(
|
|
100
|
+
recordIdToString(playbook.currentVersionId, TABLES.PLAYBOOK_VERSION),
|
|
101
|
+
TABLES.PLAYBOOK_VERSION,
|
|
102
|
+
),
|
|
103
|
+
},
|
|
104
|
+
PlaybookVersionSchema,
|
|
105
|
+
)
|
|
106
|
+
if (!currentVersion?.parentVersionId) return null
|
|
107
|
+
|
|
108
|
+
// Mark current version as rolled-back
|
|
109
|
+
await databaseService.update(
|
|
110
|
+
TABLES.PLAYBOOK_VERSION,
|
|
111
|
+
ensureRecordId(recordIdToString(currentVersion.id, TABLES.PLAYBOOK_VERSION), TABLES.PLAYBOOK_VERSION),
|
|
112
|
+
{ status: 'rolled-back' },
|
|
113
|
+
PlaybookVersionSchema,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
// Restore parent version as active
|
|
117
|
+
const parentVersion = await databaseService.findOne(
|
|
118
|
+
TABLES.PLAYBOOK_VERSION,
|
|
119
|
+
{
|
|
120
|
+
id: ensureRecordId(
|
|
121
|
+
recordIdToString(currentVersion.parentVersionId, TABLES.PLAYBOOK_VERSION),
|
|
122
|
+
TABLES.PLAYBOOK_VERSION,
|
|
123
|
+
),
|
|
124
|
+
},
|
|
125
|
+
PlaybookVersionSchema,
|
|
126
|
+
)
|
|
127
|
+
if (!parentVersion) return null
|
|
128
|
+
|
|
129
|
+
await databaseService.update(
|
|
130
|
+
TABLES.PLAYBOOK_VERSION,
|
|
131
|
+
ensureRecordId(recordIdToString(parentVersion.id, TABLES.PLAYBOOK_VERSION), TABLES.PLAYBOOK_VERSION),
|
|
132
|
+
{ status: 'active' },
|
|
133
|
+
PlaybookVersionSchema,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
await databaseService.update(
|
|
137
|
+
TABLES.PLAYBOOK,
|
|
138
|
+
ensureRecordId(playbookId, TABLES.PLAYBOOK),
|
|
139
|
+
{ currentVersionId: parentVersion.id, previousVersionId: parentVersion.parentVersionId },
|
|
140
|
+
PlaybookSchema,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
// Check if parent version is also regressed (caller can evaluate and re-invoke)
|
|
144
|
+
if (parentVersion.qualityScore !== undefined && parentVersion.qualityScore < 0.5) {
|
|
145
|
+
return this.rollbackRecursive(playbookId, organizationId, remainingLevels - 1)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return parentVersion
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export const adaptivePlaybookService = new AdaptivePlaybookService()
|