@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,16 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { serverLogger } from '../config/logger'
|
|
4
|
-
import { getRedisConnectionForBullMQ } from '../redis'
|
|
5
|
-
import {
|
|
6
|
-
attachWorkerEvents,
|
|
7
|
-
DEFAULT_JOB_RETENTION,
|
|
8
|
-
getWorkerPath,
|
|
9
|
-
LONG_JOB_LOCK_DURATION_MS,
|
|
10
|
-
createWorkerShutdown,
|
|
11
|
-
registerShutdownSignals,
|
|
12
|
-
} from '../workers/worker-utils'
|
|
13
|
-
import type { WorkerHandle } from '../workers/worker-utils'
|
|
1
|
+
import { getWorkerPath, LONG_JOB_LOCK_DURATION_MS } from '../workers/worker-utils'
|
|
2
|
+
import { createQueueFactory } from './queue-factory'
|
|
14
3
|
import {
|
|
15
4
|
buildRegularChatMemoryDigestDeduplicationId,
|
|
16
5
|
buildRegularChatMemoryDigestJobOptions,
|
|
@@ -20,50 +9,25 @@ export interface RegularChatMemoryDigestJob {
|
|
|
20
9
|
orgId: string
|
|
21
10
|
}
|
|
22
11
|
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
export async function enqueueRegularChatMemoryDigest(job: RegularChatMemoryDigestJob) {
|
|
37
|
-
return await getRegularChatMemoryDigestQueue().add(
|
|
38
|
-
'run-digest',
|
|
39
|
-
job,
|
|
40
|
-
buildRegularChatMemoryDigestJobOptions(job.orgId),
|
|
41
|
-
)
|
|
12
|
+
const regularChatMemoryDigest = createQueueFactory<RegularChatMemoryDigestJob>({
|
|
13
|
+
name: 'regular-chat-memory-digest',
|
|
14
|
+
displayName: 'Regular chat memory digest',
|
|
15
|
+
jobName: 'run-digest',
|
|
16
|
+
concurrency: 1,
|
|
17
|
+
lockDuration: LONG_JOB_LOCK_DURATION_MS,
|
|
18
|
+
defaultJobOptions: { attempts: 2, backoff: { type: 'exponential', delay: 5000 } },
|
|
19
|
+
processorPath: getWorkerPath('regular-chat-memory-digest.worker.ts'),
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
export function enqueueRegularChatMemoryDigest(job: RegularChatMemoryDigestJob) {
|
|
23
|
+
return regularChatMemoryDigest.enqueue(job, buildRegularChatMemoryDigestJobOptions(job.orgId))
|
|
42
24
|
}
|
|
43
25
|
|
|
44
26
|
export async function clearRegularChatMemoryDigestDeduplicationKey(orgId: string): Promise<void> {
|
|
45
|
-
await
|
|
27
|
+
await regularChatMemoryDigest.getQueue().removeDeduplicationKey(buildRegularChatMemoryDigestDeduplicationId(orgId))
|
|
46
28
|
}
|
|
47
29
|
|
|
48
|
-
export
|
|
49
|
-
const { registerSignals = import.meta.main } = options
|
|
50
|
-
const processorPath = getWorkerPath('regular-chat-memory-digest.worker.ts')
|
|
51
|
-
const worker = new Worker(REGULAR_CHAT_MEMORY_DIGEST_QUEUE, processorPath, {
|
|
52
|
-
connection: getRedisConnectionForBullMQ(),
|
|
53
|
-
concurrency: 1,
|
|
54
|
-
lockDuration: LONG_JOB_LOCK_DURATION_MS,
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
attachWorkerEvents(worker, 'Regular chat memory digest', serverLogger)
|
|
58
|
-
|
|
59
|
-
const shutdown = createWorkerShutdown(worker, 'Regular chat memory digest', serverLogger)
|
|
60
|
-
|
|
61
|
-
if (registerSignals) {
|
|
62
|
-
registerShutdownSignals({ name: 'Regular chat memory digest', shutdown, logger: serverLogger })
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return { worker, shutdown }
|
|
66
|
-
}
|
|
30
|
+
export const startRegularChatMemoryDigestWorker = regularChatMemoryDigest.startWorker
|
|
67
31
|
|
|
68
32
|
if (import.meta.main) {
|
|
69
33
|
startRegularChatMemoryDigestWorker()
|
|
@@ -1,58 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { serverLogger } from '../config/logger'
|
|
4
|
-
import { getRedisConnectionForBullMQ } from '../redis'
|
|
5
|
-
import {
|
|
6
|
-
attachWorkerEvents,
|
|
7
|
-
DEFAULT_JOB_RETENTION,
|
|
8
|
-
getWorkerPath,
|
|
9
|
-
LONG_JOB_LOCK_DURATION_MS,
|
|
10
|
-
createWorkerShutdown,
|
|
11
|
-
registerShutdownSignals,
|
|
12
|
-
} from '../workers/worker-utils'
|
|
13
|
-
import type { WorkerHandle } from '../workers/worker-utils'
|
|
1
|
+
import { getWorkerPath } from '../workers/worker-utils'
|
|
2
|
+
import { createQueueFactory } from './queue-factory'
|
|
14
3
|
import { buildSkillExtractionJobOptions } from './skill-extraction.config'
|
|
15
4
|
|
|
16
5
|
export interface SkillExtractionJob {
|
|
17
6
|
orgId: string
|
|
18
7
|
}
|
|
19
8
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
9
|
+
const skillExtraction = createQueueFactory<SkillExtractionJob>({
|
|
10
|
+
name: 'skill-extraction',
|
|
11
|
+
displayName: 'Skill extraction',
|
|
12
|
+
jobName: 'run-extraction',
|
|
13
|
+
concurrency: 1,
|
|
14
|
+
lockDuration: 600_000,
|
|
15
|
+
defaultJobOptions: { attempts: 2, backoff: { type: 'exponential', delay: 5000 } },
|
|
16
|
+
processorPath: getWorkerPath('skill-extraction.worker.ts'),
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export function enqueueSkillExtraction(job: SkillExtractionJob) {
|
|
20
|
+
return skillExtraction.enqueue(job, buildSkillExtractionJobOptions(job.orgId))
|
|
31
21
|
}
|
|
32
22
|
|
|
33
|
-
export
|
|
34
|
-
return await getSkillExtractionQueue().add('run-extraction', job, buildSkillExtractionJobOptions(job.orgId))
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function startSkillExtractionWorker(options: { registerSignals?: boolean } = {}): WorkerHandle {
|
|
38
|
-
const { registerSignals = import.meta.main } = options
|
|
39
|
-
const processorPath = getWorkerPath('skill-extraction.worker.ts')
|
|
40
|
-
const worker = new Worker(SKILL_EXTRACTION_QUEUE, processorPath, {
|
|
41
|
-
connection: getRedisConnectionForBullMQ(),
|
|
42
|
-
concurrency: 1,
|
|
43
|
-
lockDuration: LONG_JOB_LOCK_DURATION_MS,
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
attachWorkerEvents(worker, 'Skill extraction', serverLogger)
|
|
47
|
-
|
|
48
|
-
const shutdown = createWorkerShutdown(worker, 'Skill extraction', serverLogger)
|
|
49
|
-
|
|
50
|
-
if (registerSignals) {
|
|
51
|
-
registerShutdownSignals({ name: 'Skill extraction', shutdown, logger: serverLogger })
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return { worker, shutdown }
|
|
55
|
-
}
|
|
23
|
+
export const startSkillExtractionWorker = skillExtraction.startWorker
|
|
56
24
|
|
|
57
25
|
if (import.meta.main) {
|
|
58
26
|
startSkillExtractionWorker()
|
|
@@ -1,65 +1,33 @@
|
|
|
1
|
-
import { Queue, Worker } from 'bullmq'
|
|
2
1
|
import type { Job } from 'bullmq'
|
|
3
2
|
|
|
4
|
-
import { serverLogger } from '../config/logger'
|
|
5
3
|
import { ensureRecordId } from '../db/record-id'
|
|
6
4
|
import { databaseService } from '../db/service'
|
|
7
|
-
import { getRedisConnectionForBullMQ } from '../redis'
|
|
8
5
|
import { workstreamTitleService } from '../services/workstream-title.service'
|
|
9
|
-
import {
|
|
10
|
-
attachWorkerEvents,
|
|
11
|
-
createTracedWorkerProcessor,
|
|
12
|
-
createWorkerShutdown,
|
|
13
|
-
DEFAULT_JOB_RETENTION,
|
|
14
|
-
registerShutdownSignals,
|
|
15
|
-
} from '../workers/worker-utils'
|
|
16
|
-
import type { WorkerHandle } from '../workers/worker-utils'
|
|
6
|
+
import { createQueueFactory } from './queue-factory'
|
|
17
7
|
|
|
18
8
|
interface WorkstreamTitleGenerationJob {
|
|
19
9
|
workstreamId: string
|
|
20
10
|
sourceText: string
|
|
21
11
|
}
|
|
22
12
|
|
|
23
|
-
const WORKSTREAM_TITLE_GENERATION_QUEUE = 'workstream-title-generation'
|
|
24
|
-
|
|
25
|
-
let _workstreamTitleGenerationQueue: Queue<WorkstreamTitleGenerationJob> | null = null
|
|
26
|
-
function getWorkstreamTitleGenerationQueue(): Queue<WorkstreamTitleGenerationJob> {
|
|
27
|
-
if (!_workstreamTitleGenerationQueue) {
|
|
28
|
-
_workstreamTitleGenerationQueue = new Queue<WorkstreamTitleGenerationJob>(WORKSTREAM_TITLE_GENERATION_QUEUE, {
|
|
29
|
-
connection: getRedisConnectionForBullMQ(),
|
|
30
|
-
defaultJobOptions: { ...DEFAULT_JOB_RETENTION, attempts: 2, backoff: { type: 'exponential', delay: 2_000 } },
|
|
31
|
-
})
|
|
32
|
-
}
|
|
33
|
-
return _workstreamTitleGenerationQueue
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export async function enqueueWorkstreamTitleGeneration(job: WorkstreamTitleGenerationJob) {
|
|
37
|
-
return await getWorkstreamTitleGenerationQueue().add('generate-workstream-title', job, {
|
|
38
|
-
jobId: `workstream-title:${job.workstreamId}`,
|
|
39
|
-
})
|
|
40
|
-
}
|
|
41
|
-
|
|
42
13
|
async function processWorkstreamTitleGenerationJob(job: Job<WorkstreamTitleGenerationJob>): Promise<void> {
|
|
43
14
|
await databaseService.connect()
|
|
44
15
|
const workstreamRef = ensureRecordId(job.data.workstreamId)
|
|
45
16
|
await workstreamTitleService.generateAndPersistTitle(workstreamRef, job.data.sourceText)
|
|
46
17
|
}
|
|
47
18
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (registerSignals) {
|
|
61
|
-
registerShutdownSignals({ name: 'Workstream title generation', shutdown, logger: serverLogger })
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return { worker, shutdown }
|
|
19
|
+
const workstreamTitleGeneration = createQueueFactory<WorkstreamTitleGenerationJob>({
|
|
20
|
+
name: 'workstream-title-generation',
|
|
21
|
+
displayName: 'Workstream title generation',
|
|
22
|
+
jobName: 'generate-workstream-title',
|
|
23
|
+
concurrency: 2,
|
|
24
|
+
lockDuration: 60_000,
|
|
25
|
+
defaultJobOptions: { attempts: 2, backoff: { type: 'exponential', delay: 2_000 } },
|
|
26
|
+
processor: processWorkstreamTitleGenerationJob,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
export function enqueueWorkstreamTitleGeneration(job: WorkstreamTitleGenerationJob) {
|
|
30
|
+
return workstreamTitleGeneration.enqueue(job, { jobId: `workstream-title:${job.workstreamId}` })
|
|
65
31
|
}
|
|
32
|
+
|
|
33
|
+
export const startWorkstreamTitleGenerationWorker = workstreamTitleGeneration.startWorker
|
package/src/redis/connection.ts
CHANGED
|
@@ -58,6 +58,7 @@ class RedisConnectionManagerImpl implements RedisConnectionManager {
|
|
|
58
58
|
private healthCheckInterval: ReturnType<typeof setInterval> | null = null
|
|
59
59
|
private isInitialized = false
|
|
60
60
|
private isClosing = false
|
|
61
|
+
private isHealthCheckRunning = false
|
|
61
62
|
|
|
62
63
|
constructor(private readonly options: CreateRedisConnectionManagerOptions) {
|
|
63
64
|
this.initializeConnection()
|
|
@@ -128,6 +129,8 @@ class RedisConnectionManagerImpl implements RedisConnectionManager {
|
|
|
128
129
|
|
|
129
130
|
const intervalMs = this.options.healthCheckIntervalMs ?? DEFAULT_HEALTH_CHECK_INTERVAL_MS
|
|
130
131
|
this.healthCheckInterval = setInterval(async () => {
|
|
132
|
+
if (this.isHealthCheckRunning) return
|
|
133
|
+
this.isHealthCheckRunning = true
|
|
131
134
|
try {
|
|
132
135
|
if (this.redis && this.redis.status === 'ready') {
|
|
133
136
|
await this.redis.ping()
|
|
@@ -136,8 +139,11 @@ class RedisConnectionManagerImpl implements RedisConnectionManager {
|
|
|
136
139
|
} catch (error) {
|
|
137
140
|
log(this.options.logger, 'warn', `Redis health check failed: ${getErrorMessage(error)}`)
|
|
138
141
|
this.isHealthy = false
|
|
142
|
+
} finally {
|
|
143
|
+
this.isHealthCheckRunning = false
|
|
139
144
|
}
|
|
140
145
|
}, intervalMs)
|
|
146
|
+
this.healthCheckInterval.unref()
|
|
141
147
|
}
|
|
142
148
|
|
|
143
149
|
getConnection(): IORedis {
|
package/src/redis/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ export {
|
|
|
9
9
|
} from './connection-accessor'
|
|
10
10
|
export { withOrgMemoryLock } from './org-memory-lock'
|
|
11
11
|
export { LeaseLockLostError, withRedisLeaseLock } from './redis-lease-lock'
|
|
12
|
-
export { createWorkstreamResumableContext } from './stream-context'
|
|
12
|
+
export { closeSharedSubscriber, createWorkstreamResumableContext } from './stream-context'
|
|
13
13
|
|
|
14
14
|
export { createRedisConnectionManager }
|
|
15
15
|
export type { RedisConnectionManager }
|
|
@@ -44,6 +44,17 @@ function getSharedSubscriber(): Subscriber {
|
|
|
44
44
|
return sharedSubscriber.subscriber
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
export async function closeSharedSubscriber(): Promise<void> {
|
|
48
|
+
if (!sharedSubscriber) return
|
|
49
|
+
const { client } = sharedSubscriber
|
|
50
|
+
sharedSubscriber = undefined
|
|
51
|
+
try {
|
|
52
|
+
await client.quit()
|
|
53
|
+
} catch {
|
|
54
|
+
client.disconnect()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
47
58
|
export function createWorkstreamResumableContext() {
|
|
48
59
|
const redis = getRedisConnection()
|
|
49
60
|
return createResumableStreamContext({
|
|
@@ -1,3 +1,5 @@
|
|
|
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'
|
|
@@ -31,6 +33,30 @@ export interface AgentToolPolicy<TSkill extends PropertyKey> {
|
|
|
31
33
|
includeIndexedRepository: boolean
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
export const OWNERSHIP_DISPATCH_BLOCKED_TOOL_NAMES = Object.freeze([
|
|
37
|
+
'conversationSearch',
|
|
38
|
+
'createExecutionPlan',
|
|
39
|
+
'replaceExecutionPlan',
|
|
40
|
+
'submitExecutionNodeResult',
|
|
41
|
+
'listExecutionPlans',
|
|
42
|
+
'getExecutionPlanDetails',
|
|
43
|
+
'resumeExecutionPlanRun',
|
|
44
|
+
'consultSpecialist',
|
|
45
|
+
'consultTeam',
|
|
46
|
+
'teamThink',
|
|
47
|
+
])
|
|
48
|
+
|
|
49
|
+
function buildOwnershipDispatchArtifactPayload(artifacts: PlanArtifactSubmission[]) {
|
|
50
|
+
return artifacts.map((artifact) => ({
|
|
51
|
+
name: artifact.name,
|
|
52
|
+
kind: artifact.kind,
|
|
53
|
+
pointer: artifact.pointer,
|
|
54
|
+
...(artifact.schemaRef ? { schemaRef: artifact.schemaRef } : {}),
|
|
55
|
+
...(artifact.description ? { description: artifact.description } : {}),
|
|
56
|
+
...(artifact.payload !== undefined ? { payload: artifact.payload } : {}),
|
|
57
|
+
}))
|
|
58
|
+
}
|
|
59
|
+
|
|
34
60
|
export function toChatMode(workstreamMode: 'direct' | 'group'): ChatMode {
|
|
35
61
|
return workstreamMode === 'direct' ? 'fixedWorkstreamMode' : 'workstreamMode'
|
|
36
62
|
}
|
|
@@ -160,37 +186,96 @@ export function buildWorkstreamAgentToolPolicy<TAgent extends string, TSkill ext
|
|
|
160
186
|
}
|
|
161
187
|
}
|
|
162
188
|
|
|
163
|
-
export function buildTeamConsultationAgentToolPolicy
|
|
164
|
-
|
|
165
|
-
|
|
189
|
+
export function buildTeamConsultationAgentToolPolicy({
|
|
190
|
+
githubInstalled,
|
|
191
|
+
provideRepoTool,
|
|
192
|
+
blockedToolNames,
|
|
193
|
+
}: {
|
|
166
194
|
blockedToolNames: readonly string[]
|
|
167
195
|
githubInstalled: boolean
|
|
168
196
|
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
|
-
|
|
197
|
+
}): AgentToolPolicy<string> & { blockedToolNames: Set<string> } {
|
|
180
198
|
return {
|
|
181
199
|
resolvedMode: 'fixedWorkstreamMode',
|
|
182
|
-
skills,
|
|
183
|
-
includeMemorySearch:
|
|
184
|
-
includeConversationSearch:
|
|
200
|
+
skills: [],
|
|
201
|
+
includeMemorySearch: false,
|
|
202
|
+
includeConversationSearch: false,
|
|
185
203
|
includeMemoryRemember: false,
|
|
186
|
-
includeOrgActionSearch:
|
|
204
|
+
includeOrgActionSearch: false,
|
|
187
205
|
includeMemoryBlockAppend: false,
|
|
188
|
-
includeReadFileParts:
|
|
206
|
+
includeReadFileParts: false,
|
|
189
207
|
includeInspectWebsite: false,
|
|
190
208
|
includeProceedInOnboarding: false,
|
|
191
209
|
includeGithubIntegration: false,
|
|
192
210
|
includeIndexRepositoryByURL: false,
|
|
193
|
-
includeIndexedRepository:
|
|
194
|
-
blockedToolNames: new Set(
|
|
211
|
+
includeIndexedRepository: githubInstalled && provideRepoTool,
|
|
212
|
+
blockedToolNames: new Set(blockedToolNames),
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function buildOwnershipDispatchContextSection(params: {
|
|
217
|
+
node: PlanNodeSpec
|
|
218
|
+
resolvedInput: Record<string, unknown>
|
|
219
|
+
inputArtifacts: PlanArtifactSubmission[]
|
|
220
|
+
}): string {
|
|
221
|
+
const payload = {
|
|
222
|
+
node: {
|
|
223
|
+
id: params.node.id,
|
|
224
|
+
label: params.node.label,
|
|
225
|
+
owner: params.node.owner,
|
|
226
|
+
objective: params.node.objective,
|
|
227
|
+
instructions: params.node.instructions,
|
|
228
|
+
outputSchemaRef: params.node.outputSchemaRef ?? null,
|
|
229
|
+
deliverables: params.node.deliverables,
|
|
230
|
+
successCriteria: params.node.successCriteria,
|
|
231
|
+
completionChecks: params.node.completionChecks,
|
|
232
|
+
toolPolicy: params.node.toolPolicy,
|
|
233
|
+
contextPolicy: params.node.contextPolicy,
|
|
234
|
+
},
|
|
235
|
+
resolvedInput: params.resolvedInput,
|
|
236
|
+
inputArtifacts: buildOwnershipDispatchArtifactPayload(params.inputArtifacts),
|
|
195
237
|
}
|
|
238
|
+
|
|
239
|
+
return [
|
|
240
|
+
'<ownership-dispatch-execution>',
|
|
241
|
+
'You are executing a single isolated execution-plan node.',
|
|
242
|
+
'Do not ask the user questions. Do not reference any hidden or prior workstream chat history.',
|
|
243
|
+
'Use only the provided node context, resolved input, and input artifacts.',
|
|
244
|
+
'Return only the final structured node result that satisfies the required output contract.',
|
|
245
|
+
JSON.stringify(payload, null, 2),
|
|
246
|
+
'</ownership-dispatch-execution>',
|
|
247
|
+
].join('\n')
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function buildOwnershipDispatchResponseGuard(params: {
|
|
251
|
+
node: PlanNodeSpec
|
|
252
|
+
executionMode?: ExecutionMode
|
|
253
|
+
}): string {
|
|
254
|
+
const mode = params.executionMode ?? 'linear'
|
|
255
|
+
|
|
256
|
+
if (mode === 'linear') {
|
|
257
|
+
return [
|
|
258
|
+
'<ownership-dispatch-result-contract>',
|
|
259
|
+
'Return a single JSON object with this exact shape:',
|
|
260
|
+
'{"structuredOutput"?: object, "artifacts": Array<{ "name": string, "kind": "json"|"markdown"|"file"|"external-ref"|"record", "pointer": string, "schemaRef"?: string, "description"?: string, "payload"?: object|array }>, "notes"?: string}',
|
|
261
|
+
'Do not wrap the JSON in markdown or code fences.',
|
|
262
|
+
`Node label: ${params.node.label}`,
|
|
263
|
+
`Required deliverables: ${params.node.deliverables.length > 0 ? params.node.deliverables.map((item) => item.name).join(', ') : 'none'}`,
|
|
264
|
+
'</ownership-dispatch-result-contract>',
|
|
265
|
+
].join('\n')
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return [
|
|
269
|
+
'<ownership-dispatch-result-contract>',
|
|
270
|
+
'Produce outputs by calling the writeIntent tool for each deliverable.',
|
|
271
|
+
`Required deliverables: ${
|
|
272
|
+
params.node.deliverables
|
|
273
|
+
.filter((d) => d.required)
|
|
274
|
+
.map((d) => d.name)
|
|
275
|
+
.join(', ') || 'none'
|
|
276
|
+
}`,
|
|
277
|
+
'If writeIntent returns validation_failed, correct and re-call.',
|
|
278
|
+
'After all writes, return a brief summary.',
|
|
279
|
+
'</ownership-dispatch-result-contract>',
|
|
280
|
+
].join('\n')
|
|
196
281
|
}
|
|
@@ -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 }) }],
|
|
@@ -2,7 +2,7 @@ import { createHash, randomUUID } from 'node:crypto'
|
|
|
2
2
|
|
|
3
3
|
import type { ChatMessage } from '@lota-sdk/shared'
|
|
4
4
|
|
|
5
|
-
import { CHARS_PER_TOKEN_ESTIMATE, compactWhitespace, readRecord, readString } from '../utils/string'
|
|
5
|
+
import { CHARS_PER_TOKEN_ESTIMATE, compactWhitespace, readRecord, readString, stringifyUnknown } from '../utils/string'
|
|
6
6
|
import {
|
|
7
7
|
COMPACTION_CHUNK_MAX_CHARS,
|
|
8
8
|
CONTEXT_COMPACTION_INCLUDED_TOOL_NAMES,
|
|
@@ -112,20 +112,6 @@ function createStableId(prefix: string, ...parts: Array<string | number | undefi
|
|
|
112
112
|
return `${prefix}_${hash}`
|
|
113
113
|
}
|
|
114
114
|
|
|
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
115
|
function appendUnique(values: string[], nextValues: string[]): string[] {
|
|
130
116
|
const seen = new Set(values.map((value) => compactWhitespace(value).toLowerCase()))
|
|
131
117
|
const merged = [...values]
|
|
@@ -766,6 +752,24 @@ export function createContextCompactionRuntime(
|
|
|
766
752
|
const initialPayload = JSON.stringify([...(summaryPayload ? [summaryPayload] : []), ...remainingMessages])
|
|
767
753
|
const inputChars = initialPayload.length
|
|
768
754
|
|
|
755
|
+
const buildEarlyExitResult = (estimatedTokens: number): CompactHistoryResult => {
|
|
756
|
+
const exitSummaryPayload = buildSyntheticSummaryPayload(summaryText)
|
|
757
|
+
const outputPayload = JSON.stringify([...(exitSummaryPayload ? [exitSummaryPayload] : []), ...remainingMessages])
|
|
758
|
+
return {
|
|
759
|
+
compacted: compactedMessages.length > 0,
|
|
760
|
+
summaryText,
|
|
761
|
+
...(lastCompactedMessageId ? { lastCompactedMessageId } : {}),
|
|
762
|
+
compactedMessages,
|
|
763
|
+
compactedMessageCount: compactedMessages.length,
|
|
764
|
+
remainingMessageCount: remainingMessages.length,
|
|
765
|
+
estimatedTokens,
|
|
766
|
+
inputChars,
|
|
767
|
+
outputChars: outputPayload.length,
|
|
768
|
+
state,
|
|
769
|
+
stateDelta: mergedDelta,
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
769
773
|
for (;;) {
|
|
770
774
|
const assessment = shouldCompactHistory({
|
|
771
775
|
summaryText,
|
|
@@ -774,40 +778,12 @@ export function createContextCompactionRuntime(
|
|
|
774
778
|
})
|
|
775
779
|
|
|
776
780
|
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
|
-
}
|
|
781
|
+
return buildEarlyExitResult(assessment.estimatedTokens)
|
|
792
782
|
}
|
|
793
783
|
|
|
794
784
|
const boundary = Math.max(0, remainingMessages.length - params.tailMessageCount)
|
|
795
785
|
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
|
-
}
|
|
786
|
+
return buildEarlyExitResult(assessment.estimatedTokens)
|
|
811
787
|
}
|
|
812
788
|
|
|
813
789
|
const candidatePrefix = remainingMessages.slice(0, boundary)
|
|
@@ -816,21 +792,7 @@ export function createContextCompactionRuntime(
|
|
|
816
792
|
const sourceText = toCompactionTranscript(contextMessages)
|
|
817
793
|
|
|
818
794
|
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
|
-
}
|
|
795
|
+
return buildEarlyExitResult(assessment.estimatedTokens)
|
|
834
796
|
}
|
|
835
797
|
|
|
836
798
|
let compactionOutput = await compactContextMessages({
|