@lota-sdk/core 0.1.13 → 0.1.15
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/package.json +5 -5
- package/src/ai/embedding-cache.ts +7 -6
- package/src/ai/index.ts +1 -0
- package/src/bifrost/bifrost.ts +12 -7
- package/src/config/agent-defaults.ts +1 -1
- package/src/config/logger.ts +7 -9
- package/src/{runtime.ts → create-runtime.ts} +6 -6
- package/src/db/cursor-pagination.ts +1 -1
- package/src/db/memory-store.ts +10 -6
- package/src/db/memory.ts +6 -4
- package/src/db/schema-fingerprint.ts +1 -0
- package/src/db/service.ts +45 -51
- package/src/db/startup.ts +3 -3
- package/src/index.ts +1 -1
- package/src/queues/context-compaction.queue.ts +4 -8
- package/src/queues/document-processor.queue.ts +7 -7
- package/src/queues/memory-consolidation.queue.ts +7 -8
- package/src/queues/post-chat-memory.queue.ts +2 -6
- package/src/queues/recent-activity-title-refinement.queue.ts +2 -6
- package/src/queues/regular-chat-memory-digest.queue.ts +4 -7
- package/src/queues/skill-extraction.queue.ts +4 -7
- package/src/queues/workstream-title-generation.queue.ts +2 -6
- package/src/redis/connection.ts +6 -3
- package/src/redis/index.ts +1 -0
- package/src/redis/org-memory-lock.ts +1 -1
- package/src/redis/redis-lease-lock.ts +41 -8
- package/src/runtime/agent-stream-helpers.ts +2 -1
- package/src/runtime/context-compaction-constants.ts +1 -1
- package/src/runtime/context-compaction-runtime.ts +6 -4
- package/src/runtime/context-compaction.ts +19 -38
- package/src/runtime/execution-plan.ts +2 -2
- package/src/runtime/helper-model.ts +3 -1
- package/src/runtime/index.ts +12 -1
- package/src/runtime/memory-block.ts +3 -2
- package/src/runtime/memory-pipeline.ts +24 -5
- package/src/runtime/plugin-types.ts +1 -1
- package/src/runtime/runtime-extensions.ts +89 -13
- package/src/runtime/title-helpers.ts +11 -2
- package/src/runtime/workstream-chat-helpers.ts +5 -6
- package/src/runtime/workstream-routing-policy.ts +0 -30
- package/src/runtime/workstream-state.ts +17 -7
- package/src/services/attachment.service.ts +1 -1
- package/src/services/context-compaction.service.ts +3 -3
- package/src/services/document-chunk.service.ts +37 -32
- package/src/services/execution-plan.service.ts +2 -0
- package/src/services/learned-skill.service.ts +6 -10
- package/src/services/{memory.utils.ts → memory-utils.ts} +4 -8
- package/src/services/memory.service.ts +21 -18
- package/src/services/organization-member.service.ts +1 -1
- package/src/services/plan-artifact.service.ts +1 -0
- package/src/services/plan-executor.service.ts +2 -18
- package/src/services/plan-helpers.ts +15 -0
- package/src/services/plan-validator.service.ts +3 -18
- package/src/services/recent-activity-title.service.ts +3 -10
- package/src/services/recent-activity.service.ts +6 -12
- package/src/services/workstream-message.service.ts +26 -16
- package/src/services/workstream-title.service.ts +1 -9
- package/src/services/{workstream-turn-preparation.ts → workstream-turn-preparation.service.ts} +401 -314
- package/src/services/workstream-turn.ts +2 -2
- package/src/services/workstream.service.ts +22 -10
- package/src/services/workstream.types.ts +7 -16
- package/src/storage/attachment-storage.service.ts +4 -4
- package/src/storage/{attachments.utils.ts → attachment-utils.ts} +1 -4
- package/src/storage/index.ts +2 -2
- package/src/system-agents/{context-compacter.agent.ts → context-compaction.agent.ts} +4 -4
- package/src/system-agents/delegated-agent-factory.ts +3 -2
- package/src/system-agents/index.ts +8 -0
- package/src/system-agents/memory-reranker.agent.ts +1 -1
- package/src/system-agents/memory.agent.ts +1 -1
- package/src/system-agents/recent-activity-title-refiner.agent.ts +1 -1
- package/src/tools/execution-plan.tool.ts +6 -2
- package/src/tools/fetch-webpage.tool.ts +20 -18
- package/src/tools/index.ts +2 -2
- package/src/tools/read-file-parts.tool.ts +1 -1
- package/src/tools/search-web.tool.ts +18 -15
- package/src/tools/{search-tools.ts → search.tool.ts} +1 -1
- package/src/tools/team-think.tool.ts +9 -5
- package/src/tools/{tool-contract.ts → tool-contracts.ts} +9 -2
- package/src/utils/async.ts +1 -1
- package/src/utils/errors.ts +15 -0
- package/src/utils/hono-error-handler.ts +1 -2
- package/src/utils/index.ts +10 -2
- package/src/utils/string.ts +14 -0
- package/src/workers/bootstrap.ts +2 -2
- package/src/workers/memory-consolidation.worker.ts +12 -12
- package/src/workers/regular-chat-memory-digest.helpers.ts +2 -7
- package/src/workers/regular-chat-memory-digest.runner.ts +9 -103
- package/src/workers/skill-extraction.runner.ts +7 -101
- package/src/workers/utils/file-section-chunker.ts +5 -3
- package/src/workers/utils/workstream-message-query.ts +106 -0
- package/src/workers/worker-utils.ts +4 -0
- package/src/runtime/retrieval-pipeline.ts +0 -3
- package/src/utils/error.ts +0 -10
- /package/src/services/{context-compaction-runtime.ts → context-compaction-runtime.singleton.ts} +0 -0
- /package/src/storage/{attachments.types.ts → attachment-types.ts} +0 -0
|
@@ -4,8 +4,8 @@ import { createUIMessageStream } from 'ai'
|
|
|
4
4
|
import { lotaDebugLogger } from '../config/debug-logger'
|
|
5
5
|
import { hasApprovalRespondedParts, isApprovalContinuationRequest } from '../runtime/approval-continuation'
|
|
6
6
|
import { wrapResponseWithKeepalive } from '../utils/sse-keepalive'
|
|
7
|
-
import { prepareWorkstreamRunCore } from './workstream-turn-preparation'
|
|
8
|
-
import type { WorkstreamTurnParams, WorkstreamApprovalContinuationParams } from './workstream-turn-preparation'
|
|
7
|
+
import { prepareWorkstreamRunCore } from './workstream-turn-preparation.service'
|
|
8
|
+
import type { WorkstreamTurnParams, WorkstreamApprovalContinuationParams } from './workstream-turn-preparation.service'
|
|
9
9
|
|
|
10
10
|
export { hasApprovalRespondedParts, isApprovalContinuationRequest }
|
|
11
11
|
export { wrapResponseWithKeepalive }
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { WORKSTREAM } from '@lota-sdk/shared'
|
|
1
|
+
import { WORKSTREAM, sdkWorkstreamStatusSchema } from '@lota-sdk/shared'
|
|
2
2
|
import { BoundQuery, RecordId, StringRecordId, surql } from 'surrealdb'
|
|
3
3
|
|
|
4
4
|
import { agentDisplayNames, getCoreWorkstreamProfile, isAgentName } from '../config/agent-defaults'
|
|
5
|
+
import { serverLogger } from '../config/logger'
|
|
5
6
|
import { getWorkstreamBootstrapConfig } from '../config/workstream-defaults'
|
|
6
7
|
import { BaseService } from '../db/base.service'
|
|
7
8
|
import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
@@ -24,7 +25,7 @@ import { toIsoDateTimeString } from '../utils/date-time'
|
|
|
24
25
|
import { chatRunRegistry } from './chat-run-registry.service'
|
|
25
26
|
import { contextCompactionService } from './context-compaction.service'
|
|
26
27
|
import { workstreamMessageService } from './workstream-message.service'
|
|
27
|
-
import { WorkstreamSchema
|
|
28
|
+
import { WorkstreamSchema } from './workstream.types'
|
|
28
29
|
import type { NormalizedWorkstream, WorkstreamRecord } from './workstream.types'
|
|
29
30
|
|
|
30
31
|
// Uses SurrealQL directly to keep pagination/order logic close to queries.
|
|
@@ -368,15 +369,16 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
368
369
|
async listWorkstreams(
|
|
369
370
|
userId: RecordIdRef,
|
|
370
371
|
orgId: RecordIdRef,
|
|
371
|
-
options: { mode: string; core?: boolean; take?: number; page?: number; includeArchived
|
|
372
|
+
options: { mode: string; core?: boolean; take?: number; page?: number; includeArchived?: boolean },
|
|
372
373
|
): Promise<{ workstreams: NormalizedWorkstream[]; hasMore: boolean }> {
|
|
373
374
|
const core = options.core === true
|
|
375
|
+
const includeArchived = options.includeArchived ?? false
|
|
374
376
|
if (options.mode === 'direct' && core) {
|
|
375
377
|
throw new Error('Direct workstreams cannot be queried as core workstreams')
|
|
376
378
|
}
|
|
377
379
|
|
|
378
380
|
if (options.mode === 'direct' || core) {
|
|
379
|
-
const query =
|
|
381
|
+
const query = includeArchived
|
|
380
382
|
? LIST_ALL_WORKSTREAMS_BY_MODE_QUERY
|
|
381
383
|
: LIST_ALL_WORKSTREAMS_BY_MODE_EXCLUDE_ARCHIVED_QUERY
|
|
382
384
|
const workstreams = await databaseService.queryMany<typeof WorkstreamSchema>(
|
|
@@ -389,7 +391,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
389
391
|
|
|
390
392
|
const take = options.take ?? WORKSTREAM.DEFAULT_PAGE_LIMIT
|
|
391
393
|
const page = options.page ?? 1
|
|
392
|
-
const query =
|
|
394
|
+
const query = includeArchived ? LIST_WORKSTREAMS_QUERY : LIST_WORKSTREAMS_EXCLUDE_ARCHIVED_QUERY
|
|
393
395
|
const workstreams = await databaseService.queryMany<typeof WorkstreamSchema>(
|
|
394
396
|
new BoundQuery(query, {
|
|
395
397
|
userId,
|
|
@@ -467,7 +469,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
467
469
|
}
|
|
468
470
|
|
|
469
471
|
async updateStatus(workstreamId: RecordIdRef, status: string): Promise<NormalizedWorkstream> {
|
|
470
|
-
const validStatus =
|
|
472
|
+
const validStatus = sdkWorkstreamStatusSchema.parse(status)
|
|
471
473
|
const existing = await this.getById(workstreamId)
|
|
472
474
|
this.assertMutableWorkstream(existing, validStatus === 'archived' ? 'archive' : 'unarchive')
|
|
473
475
|
const workstream = await this.update(workstreamId, { status: validStatus })
|
|
@@ -543,11 +545,19 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
543
545
|
return false
|
|
544
546
|
}
|
|
545
547
|
|
|
546
|
-
async
|
|
548
|
+
async markCompacting(workstreamId: RecordIdRef): Promise<void> {
|
|
547
549
|
const workstreamRef = ensureRecordId(workstreamId, TABLES.WORKSTREAM)
|
|
548
550
|
await databaseService.query<unknown>(surql`
|
|
549
551
|
UPDATE ONLY ${workstreamRef}
|
|
550
|
-
SET isCompacting = ${
|
|
552
|
+
SET isCompacting = ${true}
|
|
553
|
+
`)
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
async clearCompacting(workstreamId: RecordIdRef): Promise<void> {
|
|
557
|
+
const workstreamRef = ensureRecordId(workstreamId, TABLES.WORKSTREAM)
|
|
558
|
+
await databaseService.query<unknown>(surql`
|
|
559
|
+
UPDATE ONLY ${workstreamRef}
|
|
560
|
+
SET isCompacting = ${false}
|
|
551
561
|
`)
|
|
552
562
|
}
|
|
553
563
|
|
|
@@ -566,7 +576,9 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
566
576
|
await this.update(workstreamRef, { memoryBlock: serialized })
|
|
567
577
|
|
|
568
578
|
if (updatedEntries.length >= MEMORY_BLOCK_COMPACTION_TRIGGER_ENTRIES) {
|
|
569
|
-
void this.compactMemoryBlock(workstreamRef).catch(() => {
|
|
579
|
+
void this.compactMemoryBlock(workstreamRef).catch((err: unknown) => {
|
|
580
|
+
serverLogger.warn`Memory block compaction failed for ${workstreamRef}: ${err}`
|
|
581
|
+
})
|
|
570
582
|
}
|
|
571
583
|
|
|
572
584
|
return this.formatMemoryBlockForPrompt({
|
|
@@ -612,7 +624,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
612
624
|
orgId: RecordIdRef
|
|
613
625
|
excludeWorkstreamId?: RecordIdRef
|
|
614
626
|
limit: number
|
|
615
|
-
}) {
|
|
627
|
+
}): Promise<NormalizedWorkstream[]> {
|
|
616
628
|
let excludeCondition = ''
|
|
617
629
|
const vars: Record<string, unknown> = { userId, orgId, limit }
|
|
618
630
|
|
|
@@ -1,16 +1,7 @@
|
|
|
1
|
+
import { sdkWorkstreamStatusSchema } from '@lota-sdk/shared'
|
|
1
2
|
import { z } from 'zod'
|
|
2
3
|
|
|
3
|
-
export interface Citation {
|
|
4
|
-
title?: string
|
|
5
|
-
url?: string
|
|
6
|
-
snippet?: string
|
|
7
|
-
source?: string
|
|
8
|
-
sourceId?: string
|
|
9
|
-
retrievedAt?: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
4
|
const WorkstreamModeSchema = z.enum(['direct', 'group'])
|
|
13
|
-
export const WorkstreamStatusSchema = z.enum(['regular', 'archived'])
|
|
14
5
|
const CoreWorkstreamTypeSchema = z.string()
|
|
15
6
|
|
|
16
7
|
export interface NormalizedWorkstream {
|
|
@@ -20,7 +11,7 @@ export interface NormalizedWorkstream {
|
|
|
20
11
|
mode: 'direct' | 'group'
|
|
21
12
|
core: boolean
|
|
22
13
|
coreType?: string
|
|
23
|
-
nameGenerated: boolean
|
|
14
|
+
nameGenerated: boolean // Ideally `isNameGenerated`, but maps directly to SurrealDB column `nameGenerated`
|
|
24
15
|
isRunning: boolean
|
|
25
16
|
isCompacting: boolean
|
|
26
17
|
agentId?: string | null
|
|
@@ -32,27 +23,27 @@ export interface NormalizedWorkstream {
|
|
|
32
23
|
}
|
|
33
24
|
|
|
34
25
|
export const WorkstreamSchema = z.object({
|
|
35
|
-
id: z.
|
|
26
|
+
id: z.unknown(), // SurrealDB RecordId — validated structurally at DB boundary
|
|
36
27
|
mode: WorkstreamModeSchema.optional().default('group'),
|
|
37
28
|
core: z.boolean().optional().default(false),
|
|
38
29
|
coreType: CoreWorkstreamTypeSchema.nullish(),
|
|
39
30
|
agentId: z.string().nullish(),
|
|
40
31
|
title: z.string().nullish(),
|
|
41
|
-
status:
|
|
32
|
+
status: sdkWorkstreamStatusSchema.nullish(),
|
|
42
33
|
memoryBlock: z.string().nullish(),
|
|
43
34
|
memoryBlockSummary: z.string().nullish(),
|
|
44
35
|
activeRunId: z.string().nullish(),
|
|
45
36
|
activeStreamId: z.string().nullish(),
|
|
46
37
|
compactionSummary: z.string().nullish(),
|
|
47
38
|
lastCompactedMessageId: z.string().nullish(),
|
|
48
|
-
nameGenerated: z.boolean().optional().default(false),
|
|
39
|
+
nameGenerated: z.boolean().optional().default(false), // Ideally `isNameGenerated`, but maps directly to SurrealDB column `nameGenerated`
|
|
49
40
|
isCompacting: z.boolean().optional(),
|
|
50
41
|
state: z.unknown().optional(),
|
|
51
42
|
turnCount: z.number().int().optional().default(0),
|
|
52
43
|
createdAt: z.coerce.date(),
|
|
53
44
|
updatedAt: z.coerce.date(),
|
|
54
|
-
userId: z.
|
|
55
|
-
organizationId: z.
|
|
45
|
+
userId: z.unknown(), // SurrealDB RecordId — validated structurally at DB boundary
|
|
46
|
+
organizationId: z.unknown(), // SurrealDB RecordId — validated structurally at DB boundary
|
|
56
47
|
})
|
|
57
48
|
|
|
58
49
|
export type WorkstreamRecord = z.infer<typeof WorkstreamSchema>
|
|
@@ -15,14 +15,14 @@ import type {
|
|
|
15
15
|
ReadableUploadMetadata,
|
|
16
16
|
ReadableUploadPageMode,
|
|
17
17
|
ReadableUploadPagePart,
|
|
18
|
-
} from './
|
|
18
|
+
} from './attachment-types'
|
|
19
19
|
import {
|
|
20
20
|
buildOrganizationDocumentStorageKey,
|
|
21
21
|
buildUploadStorageKey,
|
|
22
22
|
buildUploadStoragePrefix,
|
|
23
23
|
readNonNegativeInteger,
|
|
24
24
|
readRecord,
|
|
25
|
-
} from './
|
|
25
|
+
} from './attachment-utils'
|
|
26
26
|
|
|
27
27
|
const READ_FILE_PARTS_PAGES_PER_PART = 25
|
|
28
28
|
|
|
@@ -386,7 +386,7 @@ export function getAttachmentStorageService(): AttachmentStorageService {
|
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
export const attachmentStorageService = new Proxy({} as AttachmentStorageService, {
|
|
389
|
-
get(_target, prop: string) {
|
|
390
|
-
return (getAttachmentStorageService()
|
|
389
|
+
get(_target, prop: string): unknown {
|
|
390
|
+
return Reflect.get(getAttachmentStorageService(), prop)
|
|
391
391
|
},
|
|
392
392
|
})
|
|
@@ -48,10 +48,7 @@ export function buildOrganizationDocumentStorageKey(params: {
|
|
|
48
48
|
return `${buildOrganizationDocumentStoragePrefix({ orgId: params.orgId, namespace: params.namespace })}${relativePath || 'document.txt'}`
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
export
|
|
52
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) return null
|
|
53
|
-
return value as Record<string, unknown>
|
|
54
|
-
}
|
|
51
|
+
export { readRecord } from '../utils/string'
|
|
55
52
|
|
|
56
53
|
export function readNonNegativeInteger(value: unknown): number | null {
|
|
57
54
|
return Number.isInteger(value) && typeof value === 'number' && value >= 0 ? value : null
|
package/src/storage/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export * from './attachment-parser'
|
|
2
2
|
export * from './attachment-storage.service'
|
|
3
3
|
export * from './generated-document-storage.service'
|
|
4
|
-
export type { MessagePartLike, ReadableUploadPageMode, ReadableUploadPagePart } from './
|
|
4
|
+
export type { MessagePartLike, ReadableUploadPageMode, ReadableUploadPagePart } from './attachment-types'
|
|
5
5
|
export {
|
|
6
6
|
buildOrganizationDocumentStorageKey,
|
|
7
7
|
buildUploadStorageKey,
|
|
8
8
|
buildUploadStoragePrefix,
|
|
9
9
|
readNonNegativeInteger,
|
|
10
|
-
} from './
|
|
10
|
+
} from './attachment-utils'
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
|
|
9
9
|
import { resolveHelperAgentOptions } from './helper-agent-options'
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const CONTEXT_COMPACTION_PROMPT = `<agent-instructions>
|
|
12
12
|
You are a **Context Compacter** that produces both:
|
|
13
13
|
1) a dense but shorter summary of prior context
|
|
14
14
|
2) a structured state delta for durable execution continuity
|
|
@@ -36,11 +36,11 @@ Return valid data for:
|
|
|
36
36
|
</output-format>
|
|
37
37
|
</agent-instructions>`
|
|
38
38
|
|
|
39
|
-
export function
|
|
39
|
+
export function createContextCompactionAgent(options: CreateHelperToolLoopAgentOptions) {
|
|
40
40
|
return new ToolLoopAgent({
|
|
41
|
-
id: 'context-
|
|
41
|
+
id: 'context-compaction',
|
|
42
42
|
model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
|
|
43
43
|
providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
44
|
-
...resolveHelperAgentOptions(options, { instructions:
|
|
44
|
+
...resolveHelperAgentOptions(options, { instructions: CONTEXT_COMPACTION_PROMPT }),
|
|
45
45
|
})
|
|
46
46
|
}
|
|
@@ -79,6 +79,7 @@ const TERMINATION_SUFFIX = `
|
|
|
79
79
|
When your analysis is complete, return your final answer directly as markdown text. Do not leave the task unfinished or end on a tool result without a final response.
|
|
80
80
|
</termination>`
|
|
81
81
|
|
|
82
|
+
const DEFAULT_DELEGATED_AGENT_MAX_OUTPUT_TOKENS = 4096
|
|
82
83
|
const MAX_RETAINED_AGENT_MESSAGES = 10
|
|
83
84
|
const MAX_NON_SUBSTANTIVE_AGENT_RESULT_ATTEMPTS = 2
|
|
84
85
|
const NON_SUBSTANTIVE_AGENT_RESULT_RETRY_PROMPT =
|
|
@@ -164,7 +165,7 @@ export function createDelegatedAgentTool(definition: DelegatedAgentDefinition):
|
|
|
164
165
|
...(definition.providerOptions ? { providerOptions: definition.providerOptions } : {}),
|
|
165
166
|
instructions: buildDelegatedAgentInstructions(definition.instructions, agentTools),
|
|
166
167
|
tools: agentTools,
|
|
167
|
-
maxOutputTokens: definition.maxOutputTokens ??
|
|
168
|
+
maxOutputTokens: definition.maxOutputTokens ?? DEFAULT_DELEGATED_AGENT_MAX_OUTPUT_TOKENS,
|
|
168
169
|
...(typeof temperature === 'number' ? { temperature } : {}),
|
|
169
170
|
stopWhen: [stepCountIs(maxSteps)],
|
|
170
171
|
prepareStep: async ({ messages }) => ({ messages: retainCriticalAgentMessages(messages) }),
|
|
@@ -205,7 +206,7 @@ export function createDelegatedAgentToolWithContext<TContext>(
|
|
|
205
206
|
...(definition.providerOptions ? { providerOptions: definition.providerOptions } : {}),
|
|
206
207
|
instructions: buildDelegatedAgentInstructions(definition.instructions, agentTools),
|
|
207
208
|
tools: agentTools,
|
|
208
|
-
maxOutputTokens: definition.maxOutputTokens ??
|
|
209
|
+
maxOutputTokens: definition.maxOutputTokens ?? DEFAULT_DELEGATED_AGENT_MAX_OUTPUT_TOKENS,
|
|
209
210
|
...(typeof temperature === 'number' ? { temperature } : {}),
|
|
210
211
|
stopWhen: [stepCountIs(maxSteps)],
|
|
211
212
|
prepareStep: async ({ messages }) => ({ messages: retainCriticalAgentMessages(messages) }),
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
export * from './agent-result'
|
|
2
|
+
export * from './context-compaction.agent'
|
|
2
3
|
export * from './delegated-agent-factory'
|
|
3
4
|
export * from './helper-agent-options'
|
|
5
|
+
export * from './memory-reranker.agent'
|
|
6
|
+
export * from './memory.agent'
|
|
4
7
|
export * from './recent-activity-title-refiner.agent'
|
|
8
|
+
export * from './regular-chat-memory-digest.agent'
|
|
9
|
+
export * from './researcher.agent'
|
|
10
|
+
export * from './skill-extractor.agent'
|
|
11
|
+
export * from './skill-manager.agent'
|
|
12
|
+
export * from './title-generator.agent'
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
|
|
9
9
|
import { resolveHelperAgentOptions } from './helper-agent-options'
|
|
10
10
|
|
|
11
|
-
export const
|
|
11
|
+
export const MEMORY_RERANKER_PROMPT = `<agent-instructions>
|
|
12
12
|
You are a **Memory Reranker** that selects and organizes the most relevant memories for a user query.
|
|
13
13
|
|
|
14
14
|
<task>
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
|
|
9
9
|
import { resolveHelperAgentOptions } from './helper-agent-options'
|
|
10
10
|
|
|
11
|
-
export const
|
|
11
|
+
export const ORG_MEMORY_PROMPT = `<agent-instructions>
|
|
12
12
|
You are an **Organization Fact Extractor** that captures only durable, explicitly stated organization facts.
|
|
13
13
|
|
|
14
14
|
<task>
|
|
@@ -45,7 +45,7 @@ Return only the title text. No quotes, labels, JSON, markdown, or explanation.
|
|
|
45
45
|
</agent-instructions>`
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
export const
|
|
48
|
+
export const RECENT_ACTIVITY_TITLE_REFINER_PROMPT = `<agent-instructions>
|
|
49
49
|
You are the lead agent writing the visible title for a recent activity item.
|
|
50
50
|
|
|
51
51
|
<goal>
|
|
@@ -11,9 +11,13 @@ import { tool } from 'ai'
|
|
|
11
11
|
import type { RecordIdRef } from '../db/record-id'
|
|
12
12
|
import { executionPlanService } from '../services/execution-plan.service'
|
|
13
13
|
|
|
14
|
+
function isExecutionPlanResult(value: unknown): value is ExecutionPlanToolResultData {
|
|
15
|
+
return value !== null && value !== undefined && typeof value === 'object' && 'hasPlan' in value
|
|
16
|
+
}
|
|
17
|
+
|
|
14
18
|
function getLatestExecutionPlanToolResult(output: unknown): ExecutionPlanToolResultData | undefined {
|
|
15
|
-
if (output
|
|
16
|
-
return output
|
|
19
|
+
if (isExecutionPlanResult(output)) {
|
|
20
|
+
return output
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
if (Array.isArray(output)) {
|
|
@@ -2,10 +2,10 @@ import { tool } from 'ai'
|
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
|
|
4
4
|
import type { ToolDefinition } from '../ai/definitions'
|
|
5
|
-
import type { Citation } from '../services/workstream.types'
|
|
6
5
|
import { withTimeout } from '../utils/async'
|
|
7
6
|
import { readStringField, truncateOptionalText } from '../utils/string'
|
|
8
7
|
import { getFirecrawlClient } from './firecrawl-client'
|
|
8
|
+
import type { WebCitation } from './tool-contracts'
|
|
9
9
|
|
|
10
10
|
const TOOL_TIMEOUT_MS = 30_000
|
|
11
11
|
const FormatSchema = z.enum(['markdown', 'html', 'rawHtml', 'links', 'images', 'screenshot', 'summary'])
|
|
@@ -14,6 +14,11 @@ const MAX_SUMMARY_CHARS = 1_200
|
|
|
14
14
|
const MAX_LINKS = 25
|
|
15
15
|
const MAX_IMAGES = 10
|
|
16
16
|
|
|
17
|
+
function toRecord(value: unknown): Record<string, unknown> | null {
|
|
18
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null
|
|
19
|
+
return value as Record<string, unknown>
|
|
20
|
+
}
|
|
21
|
+
|
|
17
22
|
function readStringList(record: Record<string, unknown>, key: string, maxItems: number): string[] {
|
|
18
23
|
const value = record[key]
|
|
19
24
|
if (!Array.isArray(value)) return []
|
|
@@ -24,15 +29,12 @@ function readStringList(record: Record<string, unknown>, key: string, maxItems:
|
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
function summarizeDocument(url: string, document: unknown): Record<string, unknown> {
|
|
27
|
-
|
|
32
|
+
const record = toRecord(document)
|
|
33
|
+
if (!record) {
|
|
28
34
|
return { url }
|
|
29
35
|
}
|
|
30
36
|
|
|
31
|
-
const
|
|
32
|
-
const metadata =
|
|
33
|
-
record.metadata && typeof record.metadata === 'object' && !Array.isArray(record.metadata)
|
|
34
|
-
? (record.metadata as Record<string, unknown>)
|
|
35
|
-
: {}
|
|
37
|
+
const metadata = toRecord(record.metadata) ?? {}
|
|
36
38
|
|
|
37
39
|
const canonicalUrl = truncateOptionalText(
|
|
38
40
|
readStringField(metadata, 'url') ?? readStringField(metadata, 'sourceURL') ?? readStringField(record, 'url') ?? url,
|
|
@@ -51,8 +53,8 @@ function summarizeDocument(url: string, document: unknown): Record<string, unkno
|
|
|
51
53
|
return truncateOptionalText(image, 500)
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
const imageRecord = toRecord(image)
|
|
57
|
+
if (imageRecord) {
|
|
56
58
|
return truncateOptionalText(readStringField(imageRecord, 'src') ?? readStringField(imageRecord, 'url'), 500)
|
|
57
59
|
}
|
|
58
60
|
|
|
@@ -73,18 +75,18 @@ function summarizeDocument(url: string, document: unknown): Record<string, unkno
|
|
|
73
75
|
}
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
function buildFetchCitations(url: string, document: unknown):
|
|
78
|
+
function buildFetchCitations(url: string, document: unknown): WebCitation[] {
|
|
77
79
|
const fallbackUrl = url.trim()
|
|
78
80
|
let sourceId = fallbackUrl
|
|
79
81
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (typeof
|
|
85
|
-
sourceId =
|
|
86
|
-
} else if (typeof
|
|
87
|
-
sourceId =
|
|
82
|
+
const docRecord = toRecord(document)
|
|
83
|
+
if (docRecord) {
|
|
84
|
+
const metadataRecord = toRecord(docRecord.metadata)
|
|
85
|
+
if (metadataRecord) {
|
|
86
|
+
if (typeof metadataRecord.url === 'string' && metadataRecord.url.trim().length > 0) {
|
|
87
|
+
sourceId = metadataRecord.url.trim()
|
|
88
|
+
} else if (typeof metadataRecord.sourceURL === 'string' && metadataRecord.sourceURL.trim().length > 0) {
|
|
89
|
+
sourceId = metadataRecord.sourceURL.trim()
|
|
88
90
|
}
|
|
89
91
|
}
|
|
90
92
|
}
|
package/src/tools/index.ts
CHANGED
|
@@ -5,8 +5,8 @@ export * from './memory-block.tool'
|
|
|
5
5
|
export * from './read-file-parts.tool'
|
|
6
6
|
export * from './remember-memory.tool'
|
|
7
7
|
export * from './research-topic.tool'
|
|
8
|
-
export * from './search
|
|
8
|
+
export * from './search.tool'
|
|
9
9
|
export * from './search-web.tool'
|
|
10
10
|
export * from './team-think.tool'
|
|
11
|
-
export * from './tool-
|
|
11
|
+
export * from './tool-contracts'
|
|
12
12
|
export * from './user-questions.tool'
|
|
@@ -3,7 +3,7 @@ import { z } from 'zod'
|
|
|
3
3
|
|
|
4
4
|
import type { ToolDefinition } from '../ai/definitions'
|
|
5
5
|
import { attachmentStorageService } from '../storage/attachment-storage.service'
|
|
6
|
-
import type { ReadableUploadMetadata } from '../storage/
|
|
6
|
+
import type { ReadableUploadMetadata } from '../storage/attachment-types'
|
|
7
7
|
|
|
8
8
|
const PAGES_PER_PART = 25
|
|
9
9
|
|
|
@@ -2,22 +2,26 @@ import { tool } from 'ai'
|
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
|
|
4
4
|
import type { ToolDefinition } from '../ai/definitions'
|
|
5
|
-
import type { Citation } from '../services/workstream.types'
|
|
6
5
|
import { withTimeout } from '../utils/async'
|
|
7
6
|
import { readStringField, truncateOptionalText } from '../utils/string'
|
|
8
7
|
import { getFirecrawlClient } from './firecrawl-client'
|
|
8
|
+
import type { WebCitation } from './tool-contracts'
|
|
9
9
|
|
|
10
10
|
const TOOL_TIMEOUT_MS = 30_000
|
|
11
11
|
const SourceSchema = z.enum(['web', 'news', 'images'])
|
|
12
12
|
const MAX_RESULTS_PER_SOURCE = 4
|
|
13
13
|
const MAX_SNIPPET_CHARS = 320
|
|
14
14
|
|
|
15
|
+
function toRecord(value: unknown): Record<string, unknown> | null {
|
|
16
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null
|
|
17
|
+
return value as Record<string, unknown>
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
function readMetadata(item: unknown): Record<string, unknown> {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
: {}
|
|
21
|
+
const record = toRecord(item)
|
|
22
|
+
if (!record) return {}
|
|
23
|
+
const metadata = toRecord(record.metadata)
|
|
24
|
+
return metadata ?? {}
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
function readSearchItemSnippet(record: Record<string, unknown>, metadata: Record<string, unknown>): string | undefined {
|
|
@@ -36,9 +40,9 @@ function readSearchItemSnippet(record: Record<string, unknown>, metadata: Record
|
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
function summarizeSearchItem(item: unknown): Record<string, string> | null {
|
|
39
|
-
|
|
43
|
+
const record = toRecord(item)
|
|
44
|
+
if (!record) return null
|
|
40
45
|
|
|
41
|
-
const record = item as Record<string, unknown>
|
|
42
46
|
const metadata = readMetadata(item)
|
|
43
47
|
const url = extractUrlFromSearchItem(item)
|
|
44
48
|
|
|
@@ -79,9 +83,9 @@ function summarizeSearchItems(items: unknown[] | undefined): Record<string, unkn
|
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
function extractUrlFromSearchItem(item: unknown): string | undefined {
|
|
82
|
-
|
|
86
|
+
const record = toRecord(item)
|
|
87
|
+
if (!record) return undefined
|
|
83
88
|
|
|
84
|
-
const record = item as Record<string, unknown>
|
|
85
89
|
if (typeof record.url === 'string' && record.url.trim().length > 0) {
|
|
86
90
|
return record.url.trim()
|
|
87
91
|
}
|
|
@@ -89,9 +93,8 @@ function extractUrlFromSearchItem(item: unknown): string | undefined {
|
|
|
89
93
|
return record.imageUrl.trim()
|
|
90
94
|
}
|
|
91
95
|
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
94
|
-
const metadataRecord = metadata as Record<string, unknown>
|
|
96
|
+
const metadataRecord = toRecord(record.metadata)
|
|
97
|
+
if (metadataRecord) {
|
|
95
98
|
if (typeof metadataRecord.url === 'string' && metadataRecord.url.trim().length > 0) {
|
|
96
99
|
return metadataRecord.url.trim()
|
|
97
100
|
}
|
|
@@ -106,9 +109,9 @@ function extractUrlFromSearchItem(item: unknown): string | undefined {
|
|
|
106
109
|
return undefined
|
|
107
110
|
}
|
|
108
111
|
|
|
109
|
-
function buildWebCitations(results: { web?: unknown[]; news?: unknown[]; images?: unknown[] }):
|
|
112
|
+
function buildWebCitations(results: { web?: unknown[]; news?: unknown[]; images?: unknown[] }): WebCitation[] {
|
|
110
113
|
const seen = new Set<string>()
|
|
111
|
-
const citations:
|
|
114
|
+
const citations: WebCitation[] = []
|
|
112
115
|
const retrievedAt = new Date().toISOString()
|
|
113
116
|
|
|
114
117
|
const append = (items: unknown[] | undefined) => {
|
|
@@ -22,7 +22,7 @@ export function createMemorySearchTool(
|
|
|
22
22
|
const normalizedQuery = query.trim()
|
|
23
23
|
const retrieval = await memoryService.searchAllMemoriesBatched({
|
|
24
24
|
orgId: orgIdString,
|
|
25
|
-
agentName: isAgentName(agentName) ?
|
|
25
|
+
agentName: isAgentName(agentName) ? agentName : undefined,
|
|
26
26
|
query: normalizedQuery,
|
|
27
27
|
...(typeof options?.fastMode === 'boolean' ? { fastMode: options.fastMode } : {}),
|
|
28
28
|
...(typeof options?.allowMultiScopeRerank === 'boolean'
|
|
@@ -9,18 +9,21 @@ import { recordIdToString } from '../db/record-id'
|
|
|
9
9
|
import { TABLES } from '../db/tables'
|
|
10
10
|
import { mergeInstructionSections } from '../runtime/instruction-sections'
|
|
11
11
|
import { getRuntimeAdapters } from '../runtime/runtime-extensions'
|
|
12
|
+
import type { LotaRuntimeTeamThinkToolsParams } from '../runtime/runtime-extensions'
|
|
12
13
|
import { createConsultTeamTool as createConsultTeamToolSdk } from '../runtime/team-consultation-orchestrator'
|
|
13
14
|
import type { DefaultRepoSections, TeamConsultationParticipantRunner } from '../runtime/team-consultation-orchestrator'
|
|
14
15
|
import { buildTeamConsultationResponseGuard } from '../runtime/team-consultation-prompts'
|
|
15
16
|
import type { ReadableUploadMetadata } from '../services/attachment.service'
|
|
16
17
|
|
|
17
|
-
async function buildTeamThinkAgentTools(
|
|
18
|
+
async function buildTeamThinkAgentTools(
|
|
19
|
+
params: LotaRuntimeTeamThinkToolsParams,
|
|
20
|
+
): Promise<{ tools: Record<string, unknown> }> {
|
|
18
21
|
const builder = getRuntimeAdapters().workstream?.buildTeamThinkAgentTools
|
|
19
22
|
if (!builder) {
|
|
20
23
|
return { tools: {} }
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
const result = await builder(params
|
|
26
|
+
const result = await builder(params)
|
|
24
27
|
return { tools: result.tools as Record<string, unknown> }
|
|
25
28
|
}
|
|
26
29
|
|
|
@@ -79,13 +82,14 @@ export function createTeamThinkTool(params: {
|
|
|
79
82
|
toolProviders: params.toolProviders,
|
|
80
83
|
})
|
|
81
84
|
const agentConfig = config as Record<string, unknown>
|
|
82
|
-
const
|
|
83
|
-
const
|
|
85
|
+
const agentId_ = typeof agentConfig.id === 'string' ? agentConfig.id : agentId
|
|
86
|
+
const maxSteps = typeof agentConfig.maxSteps === 'number' ? agentConfig.maxSteps : 10
|
|
87
|
+
const agent = createAgent[agentId_]({
|
|
84
88
|
mode: 'fixedWorkstreamMode',
|
|
85
89
|
tools,
|
|
86
90
|
extraInstructions: agentConfig.extraInstructions,
|
|
87
91
|
maxRetries: TEAM_THINK_AGENT_MAX_RETRIES,
|
|
88
|
-
stopWhen: [stepCountIs(
|
|
92
|
+
stopWhen: [stepCountIs(maxSteps)],
|
|
89
93
|
})
|
|
90
94
|
const observer = {
|
|
91
95
|
run: async <T>(fn: () => T | Promise<T>): Promise<T> => fn(),
|
|
@@ -7,7 +7,6 @@ export const MutatingApprovalSchema = {
|
|
|
7
7
|
approvalMessageId: z.string().trim().min(1).max(200).optional(),
|
|
8
8
|
} as const
|
|
9
9
|
|
|
10
|
-
/** @lintignore */
|
|
11
10
|
export const CitationSchema = z
|
|
12
11
|
.object({
|
|
13
12
|
source: z.string().trim().min(1),
|
|
@@ -17,5 +16,13 @@ export const CitationSchema = z
|
|
|
17
16
|
})
|
|
18
17
|
.strict()
|
|
19
18
|
|
|
20
|
-
/** @lintignore */
|
|
21
19
|
export type Citation = z.infer<typeof CitationSchema>
|
|
20
|
+
|
|
21
|
+
export interface WebCitation {
|
|
22
|
+
title?: string
|
|
23
|
+
url?: string
|
|
24
|
+
snippet?: string
|
|
25
|
+
source?: string
|
|
26
|
+
sourceId?: string
|
|
27
|
+
retrievedAt?: string
|
|
28
|
+
}
|
package/src/utils/async.ts
CHANGED
package/src/utils/errors.ts
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
export function getErrorMessage(error: unknown): string {
|
|
2
|
+
if (error instanceof Error) return error.message
|
|
3
|
+
if (typeof error === 'string') return error
|
|
4
|
+
|
|
5
|
+
try {
|
|
6
|
+
return JSON.stringify(error)
|
|
7
|
+
} catch {
|
|
8
|
+
return String(error)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function toError(value: unknown): Error {
|
|
13
|
+
return value instanceof Error ? value : new Error(String(value))
|
|
14
|
+
}
|
|
15
|
+
|
|
1
16
|
export class AppError extends Error {
|
|
2
17
|
public readonly code: string
|
|
3
18
|
public readonly statusCode: number
|
|
@@ -2,8 +2,7 @@ import type { ErrorHandler } from 'hono'
|
|
|
2
2
|
import { HTTPException } from 'hono/http-exception'
|
|
3
3
|
import { ZodError } from 'zod'
|
|
4
4
|
|
|
5
|
-
import { getErrorMessage } from './
|
|
6
|
-
import { AppError } from './errors'
|
|
5
|
+
import { AppError, getErrorMessage } from './errors'
|
|
7
6
|
|
|
8
7
|
type AppErrorLike = Pick<AppError, 'code' | 'message' | 'statusCode' | 'toResponse'> & { name?: string }
|
|
9
8
|
|