@lota-sdk/core 0.1.15 → 0.1.17
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 +12 -8
- package/src/ai/definitions.ts +81 -3
- package/src/ai/embedding-cache.ts +2 -4
- package/src/ai/index.ts +0 -2
- package/src/bifrost/bifrost.ts +2 -7
- package/src/bifrost/cache-headers.ts +8 -0
- package/src/bifrost/index.ts +1 -0
- 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 +269 -178
- package/src/db/cursor-pagination.ts +3 -6
- package/src/db/index.ts +2 -0
- package/src/db/memory-store.helpers.ts +1 -3
- 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/schema-fingerprint.ts +1 -3
- 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/document-processor.queue.ts +2 -4
- 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 +20 -55
- 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/redis-lease-lock.ts +1 -2
- package/src/redis/stream-context.ts +11 -0
- package/src/runtime/agent-runtime-policy.ts +109 -35
- package/src/runtime/approval-continuation.ts +12 -6
- package/src/runtime/context-compaction-runtime.ts +1 -1
- package/src/runtime/context-compaction.ts +24 -64
- 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 +3 -1
- 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 +111 -14
- package/src/runtime/runtime-extensions.ts +2 -3
- package/src/runtime/runtime-worker-registry.ts +6 -0
- package/src/runtime/social-chat.ts +752 -0
- package/src/runtime/team-consultation-orchestrator.ts +45 -32
- 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 +292 -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 +2 -4
- 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 +27 -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 +49 -61
- 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 +28 -34
- package/src/services/skill-resolver.service.ts +19 -0
- package/src/services/social-chat-history.service.ts +197 -0
- package/src/services/system-executor.service.ts +105 -0
- package/src/services/workstream-message.service.ts +13 -37
- 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 -89
- 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/context-compaction.agent.ts +2 -0
- package/src/system-agents/delegated-agent-factory.ts +5 -0
- package/src/system-agents/memory-reranker.agent.ts +4 -2
- package/src/system-agents/memory.agent.ts +2 -0
- package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -0
- package/src/system-agents/regular-chat-memory-digest.agent.ts +2 -0
- package/src/system-agents/skill-extractor.agent.ts +2 -0
- package/src/system-agents/skill-manager.agent.ts +2 -0
- package/src/system-agents/title-generator.agent.ts +2 -0
- package/src/tools/execution-plan.tool.ts +17 -23
- package/src/tools/index.ts +0 -1
- package/src/tools/research-topic.tool.ts +2 -0
- package/src/tools/team-think.tool.ts +5 -6
- 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.helpers.ts +1 -1
- package/src/workers/regular-chat-memory-digest.runner.ts +45 -12
- package/src/workers/skill-extraction.runner.ts +26 -6
- package/src/workers/utils/file-section-chunker.ts +2 -1
- package/src/workers/utils/repo-structure-extractor.ts +2 -2
- 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 +14 -25
- package/src/workers/worker-utils.ts +2 -2
- package/src/runtime/workstream-routing-policy.ts +0 -267
- package/src/tools/log-hello-world.tool.ts +0 -17
|
@@ -1,18 +1,14 @@
|
|
|
1
|
+
import type { ExecutionMode, PlanArtifactSubmission, PlanNodeSpec } from '@lota-sdk/shared'
|
|
2
|
+
|
|
1
3
|
import { getLeadAgentId } from '../config/agent-defaults'
|
|
2
4
|
import { resolveOnboardingOwnerAgentId } from '../config/workstream-defaults'
|
|
3
5
|
import type { ChatMode } from './agent-types'
|
|
4
|
-
import { resolveReasoningProfile } from './workstream-routing-policy'
|
|
5
|
-
import type { ReasoningProfileName } from './workstream-routing-policy'
|
|
6
|
-
|
|
7
6
|
export interface AgentRuntimeConfig<TAgent extends string> {
|
|
8
7
|
id: TAgent
|
|
9
8
|
displayName: string
|
|
10
9
|
mode: ChatMode
|
|
11
10
|
extraInstructions?: string
|
|
12
11
|
maxSteps: number
|
|
13
|
-
reasoningProfile: ReasoningProfileName
|
|
14
|
-
toolCallBudget: number
|
|
15
|
-
maxInputTokensHint: number
|
|
16
12
|
}
|
|
17
13
|
|
|
18
14
|
export interface AgentToolPolicy<TSkill extends PropertyKey> {
|
|
@@ -31,6 +27,30 @@ export interface AgentToolPolicy<TSkill extends PropertyKey> {
|
|
|
31
27
|
includeIndexedRepository: boolean
|
|
32
28
|
}
|
|
33
29
|
|
|
30
|
+
export const OWNERSHIP_DISPATCH_BLOCKED_TOOL_NAMES = Object.freeze([
|
|
31
|
+
'conversationSearch',
|
|
32
|
+
'createExecutionPlan',
|
|
33
|
+
'replaceExecutionPlan',
|
|
34
|
+
'submitExecutionNodeResult',
|
|
35
|
+
'listExecutionPlans',
|
|
36
|
+
'getExecutionPlanDetails',
|
|
37
|
+
'resumeExecutionPlanRun',
|
|
38
|
+
'consultSpecialist',
|
|
39
|
+
'consultTeam',
|
|
40
|
+
'teamThink',
|
|
41
|
+
])
|
|
42
|
+
|
|
43
|
+
function buildOwnershipDispatchArtifactPayload(artifacts: PlanArtifactSubmission[]) {
|
|
44
|
+
return artifacts.map((artifact) => ({
|
|
45
|
+
name: artifact.name,
|
|
46
|
+
kind: artifact.kind,
|
|
47
|
+
pointer: artifact.pointer,
|
|
48
|
+
...(artifact.schemaRef ? { schemaRef: artifact.schemaRef } : {}),
|
|
49
|
+
...(artifact.description ? { description: artifact.description } : {}),
|
|
50
|
+
...(artifact.payload !== undefined ? { payload: artifact.payload } : {}),
|
|
51
|
+
}))
|
|
52
|
+
}
|
|
53
|
+
|
|
34
54
|
export function toChatMode(workstreamMode: 'direct' | 'group'): ChatMode {
|
|
35
55
|
return workstreamMode === 'direct' ? 'fixedWorkstreamMode' : 'workstreamMode'
|
|
36
56
|
}
|
|
@@ -74,7 +94,6 @@ export function buildAgentRuntimeConfig<TAgent extends string, TSkill extends Pr
|
|
|
74
94
|
skills?: TSkill[]
|
|
75
95
|
onboardingActive: boolean
|
|
76
96
|
linearInstalled: boolean
|
|
77
|
-
reasoningProfile?: ReasoningProfileName
|
|
78
97
|
systemWorkspaceDetails?: string
|
|
79
98
|
preSeededMemoriesSection?: string
|
|
80
99
|
retrievedKnowledgeSection?: string
|
|
@@ -89,7 +108,6 @@ export function buildAgentRuntimeConfig<TAgent extends string, TSkill extends Pr
|
|
|
89
108
|
buildOnboardingPromptSection: () => string
|
|
90
109
|
}): AgentRuntimeConfig<TAgent> {
|
|
91
110
|
const mode = params.mode ?? toChatMode(params.workstreamMode)
|
|
92
|
-
const profile = resolveReasoningProfile({ message: '', explicitProfile: params.reasoningProfile ?? 'standard' })
|
|
93
111
|
const rulesSection = params.buildGlobalRuleInstructionSection()
|
|
94
112
|
const skillsSection =
|
|
95
113
|
params.skills && params.skills.length > 0 ? params.buildSkillInstructionSection(params.skills) : ''
|
|
@@ -115,10 +133,7 @@ export function buildAgentRuntimeConfig<TAgent extends string, TSkill extends Pr
|
|
|
115
133
|
displayName: params.displayNameByAgent[params.agentId] ?? params.agentId,
|
|
116
134
|
mode,
|
|
117
135
|
extraInstructions,
|
|
118
|
-
maxSteps:
|
|
119
|
-
reasoningProfile: profile.name,
|
|
120
|
-
toolCallBudget: profile.toolCallBudget,
|
|
121
|
-
maxInputTokensHint: profile.maxInputTokensHint,
|
|
136
|
+
maxSteps: 15,
|
|
122
137
|
}
|
|
123
138
|
}
|
|
124
139
|
|
|
@@ -154,43 +169,102 @@ export function buildWorkstreamAgentToolPolicy<TAgent extends string, TSkill ext
|
|
|
154
169
|
includeReadFileParts: true,
|
|
155
170
|
includeInspectWebsite: params.onboardingActive && params.agentId === onboardingOwnerAgentId,
|
|
156
171
|
includeProceedInOnboarding: params.onboardingActive && params.agentId === onboardingOwnerAgentId,
|
|
157
|
-
includeGithubIntegration: params.
|
|
158
|
-
includeIndexRepositoryByURL: params.
|
|
172
|
+
includeGithubIntegration: params.agentId === onboardingOwnerAgentId,
|
|
173
|
+
includeIndexRepositoryByURL: params.agentId === onboardingOwnerAgentId,
|
|
159
174
|
includeIndexedRepository: params.githubInstalled && params.provideRepoTool,
|
|
160
175
|
}
|
|
161
176
|
}
|
|
162
177
|
|
|
163
|
-
export function buildTeamConsultationAgentToolPolicy
|
|
164
|
-
|
|
165
|
-
|
|
178
|
+
export function buildTeamConsultationAgentToolPolicy({
|
|
179
|
+
githubInstalled,
|
|
180
|
+
provideRepoTool,
|
|
181
|
+
blockedToolNames,
|
|
182
|
+
}: {
|
|
166
183
|
blockedToolNames: readonly string[]
|
|
167
184
|
githubInstalled: boolean
|
|
168
185
|
provideRepoTool: boolean
|
|
169
|
-
|
|
170
|
-
}): AgentToolPolicy<TSkill> & { blockedToolNames: Set<string> } {
|
|
171
|
-
const skills = resolveActiveAgentSkills({
|
|
172
|
-
agentId: params.agentId,
|
|
173
|
-
workstreamMode: 'group',
|
|
174
|
-
mode: 'fixedWorkstreamMode',
|
|
175
|
-
onboardingActive: false,
|
|
176
|
-
linearInstalled: false,
|
|
177
|
-
getAgentSkills: params.getAgentSkills,
|
|
178
|
-
}).filter((skill) => !params.blockedSkills.includes(skill))
|
|
179
|
-
|
|
186
|
+
}): AgentToolPolicy<string> & { blockedToolNames: Set<string> } {
|
|
180
187
|
return {
|
|
181
188
|
resolvedMode: 'fixedWorkstreamMode',
|
|
182
|
-
skills,
|
|
183
|
-
includeMemorySearch:
|
|
184
|
-
includeConversationSearch:
|
|
189
|
+
skills: [],
|
|
190
|
+
includeMemorySearch: false,
|
|
191
|
+
includeConversationSearch: false,
|
|
185
192
|
includeMemoryRemember: false,
|
|
186
|
-
includeOrgActionSearch:
|
|
193
|
+
includeOrgActionSearch: false,
|
|
187
194
|
includeMemoryBlockAppend: false,
|
|
188
|
-
includeReadFileParts:
|
|
195
|
+
includeReadFileParts: false,
|
|
189
196
|
includeInspectWebsite: false,
|
|
190
197
|
includeProceedInOnboarding: false,
|
|
191
198
|
includeGithubIntegration: false,
|
|
192
199
|
includeIndexRepositoryByURL: false,
|
|
193
|
-
includeIndexedRepository:
|
|
194
|
-
blockedToolNames: new Set(
|
|
200
|
+
includeIndexedRepository: githubInstalled && provideRepoTool,
|
|
201
|
+
blockedToolNames: new Set(blockedToolNames),
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function buildOwnershipDispatchContextSection(params: {
|
|
206
|
+
node: PlanNodeSpec
|
|
207
|
+
resolvedInput: Record<string, unknown>
|
|
208
|
+
inputArtifacts: PlanArtifactSubmission[]
|
|
209
|
+
}): string {
|
|
210
|
+
const payload = {
|
|
211
|
+
node: {
|
|
212
|
+
id: params.node.id,
|
|
213
|
+
label: params.node.label,
|
|
214
|
+
owner: params.node.owner,
|
|
215
|
+
objective: params.node.objective,
|
|
216
|
+
instructions: params.node.instructions,
|
|
217
|
+
outputSchemaRef: params.node.outputSchemaRef ?? null,
|
|
218
|
+
deliverables: params.node.deliverables,
|
|
219
|
+
successCriteria: params.node.successCriteria,
|
|
220
|
+
completionChecks: params.node.completionChecks,
|
|
221
|
+
toolPolicy: params.node.toolPolicy,
|
|
222
|
+
contextPolicy: params.node.contextPolicy,
|
|
223
|
+
},
|
|
224
|
+
resolvedInput: params.resolvedInput,
|
|
225
|
+
inputArtifacts: buildOwnershipDispatchArtifactPayload(params.inputArtifacts),
|
|
195
226
|
}
|
|
227
|
+
|
|
228
|
+
return [
|
|
229
|
+
'<ownership-dispatch-execution>',
|
|
230
|
+
'You are executing a single isolated execution-plan node.',
|
|
231
|
+
'Do not ask the user questions. Do not reference any hidden or prior workstream chat history.',
|
|
232
|
+
'Use only the provided node context, resolved input, and input artifacts.',
|
|
233
|
+
'Return only the final structured node result that satisfies the required output contract.',
|
|
234
|
+
JSON.stringify(payload, null, 2),
|
|
235
|
+
'</ownership-dispatch-execution>',
|
|
236
|
+
].join('\n')
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function buildOwnershipDispatchResponseGuard(params: {
|
|
240
|
+
node: PlanNodeSpec
|
|
241
|
+
executionMode?: ExecutionMode
|
|
242
|
+
}): string {
|
|
243
|
+
const mode = params.executionMode ?? 'linear'
|
|
244
|
+
|
|
245
|
+
if (mode === 'linear') {
|
|
246
|
+
return [
|
|
247
|
+
'<ownership-dispatch-result-contract>',
|
|
248
|
+
'Return a single JSON object with this exact shape:',
|
|
249
|
+
'{"structuredOutput"?: object, "artifacts": Array<{ "name": string, "kind": "json"|"markdown"|"file"|"external-ref"|"record", "pointer": string, "schemaRef"?: string, "description"?: string, "payload"?: object|array }>, "notes"?: string}',
|
|
250
|
+
'Do not wrap the JSON in markdown or code fences.',
|
|
251
|
+
`Node label: ${params.node.label}`,
|
|
252
|
+
`Required deliverables: ${params.node.deliverables.length > 0 ? params.node.deliverables.map((item) => item.name).join(', ') : 'none'}`,
|
|
253
|
+
'</ownership-dispatch-result-contract>',
|
|
254
|
+
].join('\n')
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return [
|
|
258
|
+
'<ownership-dispatch-result-contract>',
|
|
259
|
+
'Produce outputs by calling the writeIntent tool for each deliverable.',
|
|
260
|
+
`Required deliverables: ${
|
|
261
|
+
params.node.deliverables
|
|
262
|
+
.filter((d) => d.required)
|
|
263
|
+
.map((d) => d.name)
|
|
264
|
+
.join(', ') || 'none'
|
|
265
|
+
}`,
|
|
266
|
+
'If writeIntent returns validation_failed, correct and re-call.',
|
|
267
|
+
'After all writes, return a brief summary.',
|
|
268
|
+
'</ownership-dispatch-result-contract>',
|
|
269
|
+
].join('\n')
|
|
196
270
|
}
|
|
@@ -45,21 +45,27 @@ export function readApprovalContinuationResponse(message: ChatMessage): Approval
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
export function isApprovalContinuationRequest(messages: ChatMessage[]): boolean {
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
let lastAssistantIndex = -1
|
|
49
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
50
|
+
if (messages[i].role === 'assistant') {
|
|
51
|
+
lastAssistantIndex = i
|
|
52
|
+
break
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (lastAssistantIndex < 0) return false
|
|
50
56
|
|
|
51
|
-
const
|
|
52
|
-
const hasUserAfter = messages.slice(lastMessageIndex + 1).some((message) => message.role === 'user')
|
|
57
|
+
const hasUserAfter = messages.slice(lastAssistantIndex + 1).some((message) => message.role === 'user')
|
|
53
58
|
if (hasUserAfter) return false
|
|
54
59
|
|
|
55
|
-
return hasApprovalRespondedParts(
|
|
60
|
+
return hasApprovalRespondedParts(messages[lastAssistantIndex])
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
const PLAN_TOOL_NAMES = new Set([
|
|
59
64
|
'createExecutionPlan',
|
|
60
65
|
'replaceExecutionPlan',
|
|
61
66
|
'submitExecutionNodeResult',
|
|
62
|
-
'
|
|
67
|
+
'listExecutionPlans',
|
|
68
|
+
'getExecutionPlanDetails',
|
|
63
69
|
'resumeExecutionPlanRun',
|
|
64
70
|
])
|
|
65
71
|
|
|
@@ -76,7 +76,7 @@ export function createWiredContextCompactionRuntime(deps: CreateContextCompactio
|
|
|
76
76
|
const newEntriesText = params.newEntriesText.trim()
|
|
77
77
|
if (!previousSummary && !newEntriesText) return ''
|
|
78
78
|
|
|
79
|
-
return
|
|
79
|
+
return helperModelRuntime.generateHelperText({
|
|
80
80
|
tag: 'memory-block-compaction',
|
|
81
81
|
createAgent: createContextCompactionAgent,
|
|
82
82
|
messages: [{ role: 'user', content: buildMemoryBlockCompactionPrompt({ previousSummary, newEntriesText }) }],
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { createHash, randomUUID } from 'node:crypto'
|
|
2
|
-
|
|
3
1
|
import type { ChatMessage } from '@lota-sdk/shared'
|
|
4
2
|
|
|
5
|
-
import { CHARS_PER_TOKEN_ESTIMATE, compactWhitespace, readRecord, readString } from '../utils/string'
|
|
3
|
+
import { CHARS_PER_TOKEN_ESTIMATE, compactWhitespace, readRecord, readString, stringifyUnknown } from '../utils/string'
|
|
6
4
|
import {
|
|
7
5
|
COMPACTION_CHUNK_MAX_CHARS,
|
|
8
6
|
CONTEXT_COMPACTION_INCLUDED_TOOL_NAMES,
|
|
@@ -108,24 +106,10 @@ function createStableId(prefix: string, ...parts: Array<string | number | undefi
|
|
|
108
106
|
.map((part) => (part === undefined ? '' : String(part)))
|
|
109
107
|
.map((part) => compactWhitespace(part))
|
|
110
108
|
.join('|')
|
|
111
|
-
const hash =
|
|
109
|
+
const hash = new Bun.CryptoHasher('sha1').update(`${prefix}|${payload}`).digest('hex').slice(0, 20)
|
|
112
110
|
return `${prefix}_${hash}`
|
|
113
111
|
}
|
|
114
112
|
|
|
115
|
-
function stringifyUnknown(value: unknown): string | null {
|
|
116
|
-
if (value === undefined) return null
|
|
117
|
-
if (typeof value === 'string') {
|
|
118
|
-
const normalized = value.trim()
|
|
119
|
-
return normalized.length > 0 ? normalized : null
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
return JSON.stringify(value)
|
|
124
|
-
} catch {
|
|
125
|
-
return null
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
113
|
function appendUnique(values: string[], nextValues: string[]): string[] {
|
|
130
114
|
const seen = new Set(values.map((value) => compactWhitespace(value).toLowerCase()))
|
|
131
115
|
const merged = [...values]
|
|
@@ -546,7 +530,7 @@ export function createContextCompactionRuntime(
|
|
|
546
530
|
options: CreateContextCompactionRuntimeOptions,
|
|
547
531
|
): ContextCompactionRuntime {
|
|
548
532
|
const now = options.now ?? (() => Date.now())
|
|
549
|
-
const randomId = options.randomId ?? (() => randomUUID())
|
|
533
|
+
const randomId = options.randomId ?? (() => crypto.randomUUID())
|
|
550
534
|
const thresholdRatio = options.thresholdRatio ?? CONTEXT_COMPACTION_THRESHOLD_RATIO
|
|
551
535
|
const outputReserveTokens = options.outputReserveTokens ?? CONTEXT_OUTPUT_RESERVE_TOKENS
|
|
552
536
|
const safetyMarginTokens = options.safetyMarginTokens ?? CONTEXT_SAFETY_MARGIN_TOKENS
|
|
@@ -766,6 +750,24 @@ export function createContextCompactionRuntime(
|
|
|
766
750
|
const initialPayload = JSON.stringify([...(summaryPayload ? [summaryPayload] : []), ...remainingMessages])
|
|
767
751
|
const inputChars = initialPayload.length
|
|
768
752
|
|
|
753
|
+
const buildEarlyExitResult = (estimatedTokens: number): CompactHistoryResult => {
|
|
754
|
+
const exitSummaryPayload = buildSyntheticSummaryPayload(summaryText)
|
|
755
|
+
const outputPayload = JSON.stringify([...(exitSummaryPayload ? [exitSummaryPayload] : []), ...remainingMessages])
|
|
756
|
+
return {
|
|
757
|
+
compacted: compactedMessages.length > 0,
|
|
758
|
+
summaryText,
|
|
759
|
+
...(lastCompactedMessageId ? { lastCompactedMessageId } : {}),
|
|
760
|
+
compactedMessages,
|
|
761
|
+
compactedMessageCount: compactedMessages.length,
|
|
762
|
+
remainingMessageCount: remainingMessages.length,
|
|
763
|
+
estimatedTokens,
|
|
764
|
+
inputChars,
|
|
765
|
+
outputChars: outputPayload.length,
|
|
766
|
+
state,
|
|
767
|
+
stateDelta: mergedDelta,
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
769
771
|
for (;;) {
|
|
770
772
|
const assessment = shouldCompactHistory({
|
|
771
773
|
summaryText,
|
|
@@ -774,40 +776,12 @@ export function createContextCompactionRuntime(
|
|
|
774
776
|
})
|
|
775
777
|
|
|
776
778
|
if (!assessment.shouldCompact) {
|
|
777
|
-
|
|
778
|
-
const outputPayload = JSON.stringify([...(summaryPayload ? [summaryPayload] : []), ...remainingMessages])
|
|
779
|
-
return {
|
|
780
|
-
compacted: compactedMessages.length > 0,
|
|
781
|
-
summaryText,
|
|
782
|
-
...(lastCompactedMessageId ? { lastCompactedMessageId } : {}),
|
|
783
|
-
compactedMessages,
|
|
784
|
-
compactedMessageCount: compactedMessages.length,
|
|
785
|
-
remainingMessageCount: remainingMessages.length,
|
|
786
|
-
estimatedTokens: assessment.estimatedTokens,
|
|
787
|
-
inputChars,
|
|
788
|
-
outputChars: outputPayload.length,
|
|
789
|
-
state,
|
|
790
|
-
stateDelta: mergedDelta,
|
|
791
|
-
}
|
|
779
|
+
return buildEarlyExitResult(assessment.estimatedTokens)
|
|
792
780
|
}
|
|
793
781
|
|
|
794
782
|
const boundary = Math.max(0, remainingMessages.length - params.tailMessageCount)
|
|
795
783
|
if (boundary <= 0) {
|
|
796
|
-
|
|
797
|
-
const outputPayload = JSON.stringify([...(summaryPayload ? [summaryPayload] : []), ...remainingMessages])
|
|
798
|
-
return {
|
|
799
|
-
compacted: compactedMessages.length > 0,
|
|
800
|
-
summaryText,
|
|
801
|
-
...(lastCompactedMessageId ? { lastCompactedMessageId } : {}),
|
|
802
|
-
compactedMessages,
|
|
803
|
-
compactedMessageCount: compactedMessages.length,
|
|
804
|
-
remainingMessageCount: remainingMessages.length,
|
|
805
|
-
estimatedTokens: assessment.estimatedTokens,
|
|
806
|
-
inputChars,
|
|
807
|
-
outputChars: outputPayload.length,
|
|
808
|
-
state,
|
|
809
|
-
stateDelta: mergedDelta,
|
|
810
|
-
}
|
|
784
|
+
return buildEarlyExitResult(assessment.estimatedTokens)
|
|
811
785
|
}
|
|
812
786
|
|
|
813
787
|
const candidatePrefix = remainingMessages.slice(0, boundary)
|
|
@@ -816,21 +790,7 @@ export function createContextCompactionRuntime(
|
|
|
816
790
|
const sourceText = toCompactionTranscript(contextMessages)
|
|
817
791
|
|
|
818
792
|
if (!compactWhitespace(sourceText)) {
|
|
819
|
-
|
|
820
|
-
const outputPayload = JSON.stringify([...(summaryPayload ? [summaryPayload] : []), ...remainingMessages])
|
|
821
|
-
return {
|
|
822
|
-
compacted: compactedMessages.length > 0,
|
|
823
|
-
summaryText,
|
|
824
|
-
...(lastCompactedMessageId ? { lastCompactedMessageId } : {}),
|
|
825
|
-
compactedMessages,
|
|
826
|
-
compactedMessageCount: compactedMessages.length,
|
|
827
|
-
remainingMessageCount: remainingMessages.length,
|
|
828
|
-
estimatedTokens: assessment.estimatedTokens,
|
|
829
|
-
inputChars,
|
|
830
|
-
outputChars: outputPayload.length,
|
|
831
|
-
state,
|
|
832
|
-
stateDelta: mergedDelta,
|
|
833
|
-
}
|
|
793
|
+
return buildEarlyExitResult(assessment.estimatedTokens)
|
|
834
794
|
}
|
|
835
795
|
|
|
836
796
|
let compactionOutput = await compactContextMessages({
|
|
@@ -2,16 +2,18 @@ import type { SerializableExecutionPlan } from '@lota-sdk/shared'
|
|
|
2
2
|
|
|
3
3
|
const EXECUTION_PLAN_AGENT_PROTOCOL_PROMPT = `<execution-plan-protocol>
|
|
4
4
|
- Before doing multi-step work, create a contract-driven execution plan instead of tracking steps only in prose.
|
|
5
|
+
- A workstream may have multiple active execution plans. Review all plans before creating new ones.
|
|
5
6
|
- Plans are graph-capable workflow contracts. Every execution node must define objective, instructions, deliverables, success criteria, completion checks, retry policy, failure policy, and tool/context policy.
|
|
6
7
|
- The runtime executor owns lifecycle truth. Do not claim that a node is complete until submitExecutionNodeResult succeeds.
|
|
7
|
-
- Use execution-plan tools to create, replace, inspect,
|
|
8
|
-
-
|
|
8
|
+
- Use execution-plan tools to create, replace, inspect, and resume runs.
|
|
9
|
+
- Visible workstream agents do not manually submit node results; dispatched execution nodes are completed by the runtime executor.
|
|
10
|
+
- Treat the active execution runs in <execution-plan-state> as authoritative. Do not mutate run or node status in prose.
|
|
9
11
|
- Work only on nodes that are active or explicitly ready for your executor. If a node is awaiting human input or approval, stop and let the runtime resume it.
|
|
10
12
|
- If the graph, contracts, or success criteria materially change, replace the plan instead of silently drifting.
|
|
11
13
|
</execution-plan-protocol>`
|
|
12
14
|
|
|
13
|
-
function
|
|
14
|
-
if (
|
|
15
|
+
function formatExecutionPlansForPrompt(plans: SerializableExecutionPlan[]): string | undefined {
|
|
16
|
+
if (plans.length === 0) return undefined
|
|
15
17
|
|
|
16
18
|
const payload = {
|
|
17
19
|
policy: {
|
|
@@ -21,46 +23,48 @@ function formatExecutionPlanForPrompt(plan: SerializableExecutionPlan | null | u
|
|
|
21
23
|
artifactsAreFirstClassOutputs: true,
|
|
22
24
|
checkpointRecoveryEnabled: true,
|
|
23
25
|
},
|
|
24
|
-
|
|
26
|
+
activePlans: plans,
|
|
27
|
+
planCount: plans.length,
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
return ['<execution-plan-state>', JSON.stringify(payload, null, 2), '</execution-plan-state>'].join('\n')
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
export function buildExecutionPlanInstructionSections(
|
|
31
|
-
|
|
34
|
+
plans: SerializableExecutionPlan[] | null | undefined,
|
|
32
35
|
): string[] | undefined {
|
|
36
|
+
const normalized = plans ?? []
|
|
33
37
|
const sections = [EXECUTION_PLAN_AGENT_PROTOCOL_PROMPT]
|
|
34
|
-
const
|
|
35
|
-
if (
|
|
36
|
-
sections.push(
|
|
38
|
+
const stateSection = formatExecutionPlansForPrompt(normalized)
|
|
39
|
+
if (stateSection) {
|
|
40
|
+
sections.push(stateSection)
|
|
37
41
|
}
|
|
38
42
|
return sections
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
export function createExecutionPlanInstructionSectionCache(params: {
|
|
42
46
|
disabled?: boolean
|
|
43
|
-
|
|
47
|
+
loadPlans: () => Promise<SerializableExecutionPlan[]>
|
|
44
48
|
}) {
|
|
45
|
-
let
|
|
49
|
+
let plansPromise: Promise<SerializableExecutionPlan[]> | null = null
|
|
46
50
|
let sectionsPromise: Promise<string[] | undefined> | null = null
|
|
47
51
|
|
|
48
52
|
return {
|
|
49
53
|
invalidate() {
|
|
50
|
-
|
|
54
|
+
plansPromise = null
|
|
51
55
|
sectionsPromise = null
|
|
52
56
|
},
|
|
53
|
-
async
|
|
54
|
-
if (params.disabled) return
|
|
57
|
+
async getPlans(): Promise<SerializableExecutionPlan[]> {
|
|
58
|
+
if (params.disabled) return []
|
|
55
59
|
|
|
56
|
-
|
|
57
|
-
return
|
|
60
|
+
plansPromise ??= params.loadPlans()
|
|
61
|
+
return plansPromise
|
|
58
62
|
},
|
|
59
63
|
async getSections(): Promise<string[] | undefined> {
|
|
60
64
|
if (params.disabled) return undefined
|
|
61
65
|
|
|
62
|
-
sectionsPromise ??= this.
|
|
63
|
-
return
|
|
66
|
+
sectionsPromise ??= this.getPlans().then((plans) => buildExecutionPlanInstructionSections(plans))
|
|
67
|
+
return sectionsPromise
|
|
64
68
|
},
|
|
65
69
|
}
|
|
66
70
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { GraphDesignRequest, GraphDesignResponse } from '@lota-sdk/shared'
|
|
2
|
+
|
|
3
|
+
export interface GraphDesigner {
|
|
4
|
+
designGraph(request: GraphDesignRequest): Promise<GraphDesignResponse>
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
let _graphDesigner: GraphDesigner | null = null
|
|
8
|
+
|
|
9
|
+
export function configureGraphDesigner(designer: GraphDesigner): void {
|
|
10
|
+
_graphDesigner = designer
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getGraphDesigner(): GraphDesigner | null {
|
|
14
|
+
return _graphDesigner
|
|
15
|
+
}
|