@lota-sdk/core 0.4.17 → 0.4.19
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 +2 -2
- package/src/ai-gateway/ai-gateway.ts +6 -25
- package/src/system-agents/delegated-agent-factory.ts +5 -4
- package/src/tools/execution-plan.tool.ts +7 -6
- package/src/tools/fetch-webpage.tool.ts +8 -5
- package/src/tools/memory-block.tool.ts +4 -2
- package/src/tools/plan-approval.tool.ts +4 -2
- package/src/tools/read-file-parts.tool.ts +8 -5
- package/src/tools/remember-memory.tool.ts +4 -2
- package/src/tools/research-topic.tool.ts +8 -4
- package/src/tools/search-web.tool.ts +8 -5
- package/src/tools/search.tool.ts +23 -4
- package/src/tools/team-think.tool.ts +4 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lota-sdk/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.19",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@ai-sdk/openai": "^3.0.53",
|
|
32
32
|
"@chat-adapter/slack": "^4.26.0",
|
|
33
33
|
"@chat-adapter/state-ioredis": "^4.26.0",
|
|
34
|
-
"@lota-sdk/shared": "0.4.
|
|
34
|
+
"@lota-sdk/shared": "0.4.19",
|
|
35
35
|
"@mendable/firecrawl-js": "^4.18.3",
|
|
36
36
|
"@surrealdb/node": "^3.0.3",
|
|
37
37
|
"ai": "^6.0.168",
|
|
@@ -21,7 +21,6 @@ type AiGatewayGenerateResult = Awaited<ReturnType<WrapStreamOptions['doGenerate'
|
|
|
21
21
|
type AiGatewayStreamResult = Awaited<ReturnType<WrapStreamOptions['doStream']>>
|
|
22
22
|
type AiGatewayGeneratedContent = AiGatewayGenerateResult['content'][number]
|
|
23
23
|
type AiGatewayStreamPart = AiGatewayStreamResult['stream'] extends ReadableStream<infer T> ? T : never
|
|
24
|
-
type AiGatewayProviderOptions = NonNullable<AiGatewayCallOptions['providerOptions']>
|
|
25
24
|
type AiGatewayAttemptResult<A> = { source: string; result: A }
|
|
26
25
|
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
|
|
27
26
|
type AiGatewayRunFork = <A, E>(effect: Effect.Effect<A, E, never>) => Fiber.Fiber<A, E | unknown>
|
|
@@ -867,10 +866,6 @@ function executeStreamAttemptPlan(
|
|
|
867
866
|
)
|
|
868
867
|
}
|
|
869
868
|
|
|
870
|
-
function isOpenRouterOpenAIReasoningModel(modelId: string): boolean {
|
|
871
|
-
return modelId.trim().toLowerCase().startsWith('openrouter/openai/gpt-5')
|
|
872
|
-
}
|
|
873
|
-
|
|
874
869
|
function shouldCloseInjectedReasoning(chunk: AiGatewayStreamPart): boolean {
|
|
875
870
|
switch (chunk.type) {
|
|
876
871
|
case 'stream-start':
|
|
@@ -983,6 +978,10 @@ export function setDefaultAiGatewayDeps(deps: AiGatewayDeps): void {
|
|
|
983
978
|
defaultAiGatewayDeps = deps
|
|
984
979
|
}
|
|
985
980
|
|
|
981
|
+
export function getDefaultAiGatewayRunPromise(): AiGatewayDeps['runPromise'] {
|
|
982
|
+
return resolveAiGatewayDeps(undefined).runPromise
|
|
983
|
+
}
|
|
984
|
+
|
|
986
985
|
function resolveAiGatewayDeps(deps: AiGatewayDeps | undefined): AiGatewayDeps {
|
|
987
986
|
if (deps) return deps
|
|
988
987
|
if (defaultAiGatewayDeps) return defaultAiGatewayDeps
|
|
@@ -1048,27 +1047,9 @@ function createAiGatewayLanguageModelMiddleware(
|
|
|
1048
1047
|
|
|
1049
1048
|
export function normalizeAiGatewayChatProviderOptions(
|
|
1050
1049
|
params: AiGatewayCallOptions,
|
|
1051
|
-
|
|
1050
|
+
_modelId?: string,
|
|
1052
1051
|
): AiGatewayCallOptions {
|
|
1053
|
-
|
|
1054
|
-
? ({ ...params.providerOptions } as AiGatewayProviderOptions)
|
|
1055
|
-
: ({} as AiGatewayProviderOptions)
|
|
1056
|
-
const openaiOptions = isRecord(providerOptions.openai)
|
|
1057
|
-
? { ...providerOptions.openai }
|
|
1058
|
-
: ({} as Record<string, unknown>)
|
|
1059
|
-
|
|
1060
|
-
if (modelId && isOpenRouterOpenAIReasoningModel(modelId) && openaiOptions.forceReasoning === undefined) {
|
|
1061
|
-
openaiOptions.forceReasoning = true
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
if (providerOptions.openai === openaiOptions || Object.keys(openaiOptions).length === 0) {
|
|
1065
|
-
return params
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
return {
|
|
1069
|
-
...params,
|
|
1070
|
-
providerOptions: { ...providerOptions, openai: openaiOptions as AiGatewayProviderOptions['openai'] },
|
|
1071
|
-
}
|
|
1052
|
+
return params
|
|
1072
1053
|
}
|
|
1073
1054
|
|
|
1074
1055
|
function withAiGatewayDevTools<TModel extends AiGatewayLanguageModel>(model: TModel): TModel {
|
|
@@ -3,6 +3,7 @@ import type { ModelMessage, LanguageModel, ToolLoopAgentSettings, ToolSet } from
|
|
|
3
3
|
import { Schema, Effect } from 'effect'
|
|
4
4
|
import { z } from 'zod'
|
|
5
5
|
|
|
6
|
+
import { getDefaultAiGatewayRunPromise } from '../ai-gateway/ai-gateway'
|
|
6
7
|
import type { ToolDefinition } from '../ai/definitions'
|
|
7
8
|
import { aiLogger } from '../config/logger'
|
|
8
9
|
import { ERROR_TAGS } from '../effect/errors'
|
|
@@ -26,7 +27,7 @@ interface DelegatedAgentDefinition {
|
|
|
26
27
|
maxSteps?: number
|
|
27
28
|
maxOutputTokens?: number
|
|
28
29
|
temperature?: number
|
|
29
|
-
runPromise
|
|
30
|
+
runPromise?: <A, E>(effect: Effect.Effect<A, E, never>, options?: { signal?: AbortSignal }) => Promise<A>
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
type DelegatedAgentRunPromise = <A, E>(
|
|
@@ -42,7 +43,7 @@ interface DelegatedAgentDefinitionWithContext<TContext> extends Omit<
|
|
|
42
43
|
createTools: (context: TContext) => ToolSet
|
|
43
44
|
/** Pull runPromise off the caller's context so each tool invocation carries
|
|
44
45
|
* its own runtime binding rather than closing over a boot-time one. */
|
|
45
|
-
getRunPromise
|
|
46
|
+
getRunPromise?: (context: TContext) => DelegatedAgentRunPromise
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
class DelegatedAgentError extends Schema.TaggedErrorClass<DelegatedAgentError>()(ERROR_TAGS.DelegatedAgentError, {
|
|
@@ -251,7 +252,7 @@ export function createDelegatedAgentTool(definition: DelegatedAgentDefinition):
|
|
|
251
252
|
description: definition.description,
|
|
252
253
|
inputSchema: z.object({ task: z.string().min(1) }),
|
|
253
254
|
execute: ({ task }: { task: string }, { abortSignal }) =>
|
|
254
|
-
definition.runPromise(
|
|
255
|
+
(definition.runPromise ?? getDefaultAiGatewayRunPromise())(
|
|
255
256
|
Effect.gen(function* () {
|
|
256
257
|
const agentTools = definition.tools
|
|
257
258
|
const createAgent = () =>
|
|
@@ -309,7 +310,7 @@ export function createDelegatedAgentToolWithContext<TContext>(
|
|
|
309
310
|
description: definition.description,
|
|
310
311
|
inputSchema: z.object({ task: z.string().min(1) }),
|
|
311
312
|
execute: ({ task }: { task: string }, { abortSignal }) =>
|
|
312
|
-
definition.getRunPromise(context)(
|
|
313
|
+
(definition.getRunPromise?.(context) ?? getDefaultAiGatewayRunPromise())(
|
|
313
314
|
Effect.gen(function* () {
|
|
314
315
|
const agentTools = definition.createTools(context)
|
|
315
316
|
const createAgent = () =>
|
|
@@ -15,6 +15,7 @@ import type { CreateProjectWithPlanResultData, ExecutionPlanArgs } from '@lota-s
|
|
|
15
15
|
import { tool } from 'ai'
|
|
16
16
|
import { Effect } from 'effect'
|
|
17
17
|
|
|
18
|
+
import { getDefaultAiGatewayRunPromise } from '../ai-gateway/ai-gateway'
|
|
18
19
|
import { serverLogger } from '../config/logger'
|
|
19
20
|
import type { RecordIdRef } from '../db/record-id'
|
|
20
21
|
import { recordIdToString } from '../db/record-id'
|
|
@@ -58,7 +59,7 @@ export function createExecutionPlanTool(params: {
|
|
|
58
59
|
agentId: string
|
|
59
60
|
executionPlanService: ExecutionPlanExecutionPlanService
|
|
60
61
|
threadService: ExecutionPlanThreadService
|
|
61
|
-
runPromise
|
|
62
|
+
runPromise?: ExecutionPlanToolRunPromise
|
|
62
63
|
onPlanChanged?: () => void
|
|
63
64
|
validateInlinePlan?: (draft: ReturnType<typeof expandAgentPlanDraft>) => void
|
|
64
65
|
}) {
|
|
@@ -67,7 +68,7 @@ export function createExecutionPlanTool(params: {
|
|
|
67
68
|
'Manage execution plans. Actions: create (inline, 1-2 nodes), create-project (dedicated project thread, 3+ nodes), replace (swap active plan), resume (resume interrupted plan), update-node (submit result for a specific node).',
|
|
68
69
|
inputSchema: ExecutionPlanArgsSchema,
|
|
69
70
|
execute: (input) =>
|
|
70
|
-
params.runPromise(
|
|
71
|
+
(params.runPromise ?? getDefaultAiGatewayRunPromise())(
|
|
71
72
|
Effect.gen(function* () {
|
|
72
73
|
const parsed = yield* parseExecutionPlanArgsEffect(input)
|
|
73
74
|
|
|
@@ -265,14 +266,14 @@ function extractAgentPlanDraftEffect(
|
|
|
265
266
|
export function createExecutionPlanQueryTool(params: {
|
|
266
267
|
threadId: RecordIdRef
|
|
267
268
|
executionPlanService: Pick<ExecutionPlanExecutionPlanService, 'listActivePlanSummaries' | 'getActivePlanToolResult'>
|
|
268
|
-
runPromise
|
|
269
|
+
runPromise?: ExecutionPlanToolRunPromise
|
|
269
270
|
}) {
|
|
270
271
|
return tool({
|
|
271
272
|
description:
|
|
272
273
|
'Query execution plans. Omit runId to list all active plans. Provide runId to load a specific plan run.',
|
|
273
274
|
inputSchema: ExecutionPlanQueryArgsSchema,
|
|
274
275
|
execute: (input) =>
|
|
275
|
-
params.runPromise(
|
|
276
|
+
(params.runPromise ?? getDefaultAiGatewayRunPromise())(
|
|
276
277
|
Effect.gen(function* () {
|
|
277
278
|
if (!input.runId) {
|
|
278
279
|
return yield* params.executionPlanService.listActivePlanSummaries(params.threadId)
|
|
@@ -295,7 +296,7 @@ export function createSubmitExecutionNodeResultTool(params: {
|
|
|
295
296
|
threadId: RecordIdRef
|
|
296
297
|
agentId: string
|
|
297
298
|
executionPlanService: Pick<ExecutionPlanExecutionPlanService, 'submitNodeResult'>
|
|
298
|
-
runPromise
|
|
299
|
+
runPromise?: ExecutionPlanToolRunPromise
|
|
299
300
|
onPlanChanged?: () => void
|
|
300
301
|
}) {
|
|
301
302
|
return tool({
|
|
@@ -303,7 +304,7 @@ export function createSubmitExecutionNodeResultTool(params: {
|
|
|
303
304
|
'Submit the result for the currently running execution node. The executor validates outputs, artifacts, and completion checks before advancing the run.',
|
|
304
305
|
inputSchema: SubmitExecutionNodeResultArgsSchema,
|
|
305
306
|
execute: (input) =>
|
|
306
|
-
params.runPromise(
|
|
307
|
+
(params.runPromise ?? getDefaultAiGatewayRunPromise())(
|
|
307
308
|
Effect.gen(function* () {
|
|
308
309
|
const result = yield* params.executionPlanService.submitNodeResult({
|
|
309
310
|
threadId: params.threadId,
|
|
@@ -3,6 +3,7 @@ import { tool } from 'ai'
|
|
|
3
3
|
import { Effect, Schema } from 'effect'
|
|
4
4
|
import { z } from 'zod'
|
|
5
5
|
|
|
6
|
+
import { getDefaultAiGatewayRunPromise } from '../ai-gateway/ai-gateway'
|
|
6
7
|
import type { ToolDefinition } from '../ai/definitions'
|
|
7
8
|
import { withTimeout } from '../utils/async'
|
|
8
9
|
import { nowIsoDateTimeString } from '../utils/date-time'
|
|
@@ -12,7 +13,7 @@ import { toRecord, WEB_TOOL_TIMEOUT_MS } from './web-tool-shared'
|
|
|
12
13
|
|
|
13
14
|
export interface FetchWebpageToolContext {
|
|
14
15
|
firecrawl: Firecrawl
|
|
15
|
-
runPromise
|
|
16
|
+
runPromise?: <A, E>(effect: Effect.Effect<A, E, never>, options?: { signal?: AbortSignal }) => Promise<A>
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
class FetchWebpageToolError extends Schema.TaggedErrorClass<FetchWebpageToolError>()(
|
|
@@ -103,8 +104,9 @@ function buildFetchCitations(url: string, document: unknown): WebCitation[] {
|
|
|
103
104
|
|
|
104
105
|
export const fetchWebpageTool = {
|
|
105
106
|
name: 'fetchWebpage',
|
|
106
|
-
create: ({ firecrawl, runPromise }: FetchWebpageToolContext) =>
|
|
107
|
-
|
|
107
|
+
create: ({ firecrawl, runPromise }: FetchWebpageToolContext) => {
|
|
108
|
+
const runEffect = runPromise ?? getDefaultAiGatewayRunPromise()
|
|
109
|
+
return tool({
|
|
108
110
|
description: 'Retrieve and parse a single webpage.',
|
|
109
111
|
inputSchema: z
|
|
110
112
|
.object({
|
|
@@ -123,7 +125,7 @@ export const fetchWebpageTool = {
|
|
|
123
125
|
}: { url: string; formats?: z.infer<typeof FormatSchema>[]; onlyMainContent?: boolean; maxAge?: number },
|
|
124
126
|
{ abortSignal }: { abortSignal?: AbortSignal } = {},
|
|
125
127
|
) =>
|
|
126
|
-
|
|
128
|
+
runEffect(
|
|
127
129
|
Effect.gen(function* () {
|
|
128
130
|
const result = yield* Effect.tryPromise({
|
|
129
131
|
try: () =>
|
|
@@ -142,5 +144,6 @@ export const fetchWebpageTool = {
|
|
|
142
144
|
}).pipe(Effect.withSpan('tool.fetchWebpage.execute')),
|
|
143
145
|
abortSignal ? { signal: abortSignal } : undefined,
|
|
144
146
|
),
|
|
145
|
-
})
|
|
147
|
+
})
|
|
148
|
+
},
|
|
146
149
|
} as const satisfies ToolDefinition<FetchWebpageToolContext>
|
|
@@ -2,6 +2,7 @@ import { tool } from 'ai'
|
|
|
2
2
|
import { Effect } from 'effect'
|
|
3
3
|
import { z } from 'zod'
|
|
4
4
|
|
|
5
|
+
import { getDefaultAiGatewayRunPromise } from '../ai-gateway/ai-gateway'
|
|
5
6
|
import type { RecordIdRef } from '../db/record-id'
|
|
6
7
|
import {
|
|
7
8
|
hasMemoryBlockEntry,
|
|
@@ -25,10 +26,11 @@ export function createMemoryBlockTool({
|
|
|
25
26
|
threadId: RecordIdRef
|
|
26
27
|
agentLabel: string
|
|
27
28
|
threadService: MemoryBlockThreadService
|
|
28
|
-
runPromise
|
|
29
|
+
runPromise?: <A, E>(effect: Effect.Effect<A, E, never>) => Promise<A>
|
|
29
30
|
getCurrentBlock?: () => string
|
|
30
31
|
onAppend?: (value: string) => void
|
|
31
32
|
}) {
|
|
33
|
+
const runEffect = runPromise ?? getDefaultAiGatewayRunPromise()
|
|
32
34
|
return tool({
|
|
33
35
|
description:
|
|
34
36
|
'Append a critical 2-3 sentence note to the conversation memory block. Include only essential information.',
|
|
@@ -50,7 +52,7 @@ export function createMemoryBlockTool({
|
|
|
50
52
|
|
|
51
53
|
void safeEnqueue(
|
|
52
54
|
() =>
|
|
53
|
-
|
|
55
|
+
runEffect(
|
|
54
56
|
Effect.gen(function* () {
|
|
55
57
|
const updated = yield* threadService.appendMemoryBlock(threadId, prepared.formatted)
|
|
56
58
|
onAppend?.(updated)
|
|
@@ -2,6 +2,7 @@ import { PlanApprovalArgsSchema, PlanApprovalToolResultDataSchema } from '@lota-
|
|
|
2
2
|
import { tool } from 'ai'
|
|
3
3
|
import { Effect } from 'effect'
|
|
4
4
|
|
|
5
|
+
import { getDefaultAiGatewayRunPromise } from '../ai-gateway/ai-gateway'
|
|
5
6
|
import type { RecordIdRef } from '../db/record-id'
|
|
6
7
|
import type { makeExecutionPlanService } from '../services/execution-plan/execution-plan.service'
|
|
7
8
|
import { toToolPlanSummary } from '../services/plan/plan-run-data'
|
|
@@ -21,15 +22,16 @@ export function createPlanApprovalTool(params: {
|
|
|
21
22
|
threadId: RecordIdRef
|
|
22
23
|
actorId: string
|
|
23
24
|
executionPlanService: PlanApprovalExecutionPlanService
|
|
24
|
-
runPromise
|
|
25
|
+
runPromise?: <A, E>(effect: Effect.Effect<A, E, never>, options?: { signal?: AbortSignal }) => Promise<A>
|
|
25
26
|
}) {
|
|
27
|
+
const runEffect = params.runPromise ?? getDefaultAiGatewayRunPromise()
|
|
26
28
|
return tool({
|
|
27
29
|
description:
|
|
28
30
|
'Approve, reject, or request changes to a plan that is pending approval. Use approve to start execution, reject to abort it, or modify to request a revised plan.',
|
|
29
31
|
inputSchema: PlanApprovalArgsSchema,
|
|
30
32
|
outputSchema: PlanApprovalToolResultDataSchema,
|
|
31
33
|
execute: (input, { abortSignal }: { abortSignal?: AbortSignal } = {}) =>
|
|
32
|
-
|
|
34
|
+
runEffect(
|
|
33
35
|
Effect.gen(function* () {
|
|
34
36
|
switch (input.action) {
|
|
35
37
|
case 'approve': {
|
|
@@ -2,6 +2,7 @@ import { tool } from 'ai'
|
|
|
2
2
|
import { Schema, Effect } from 'effect'
|
|
3
3
|
import { z } from 'zod'
|
|
4
4
|
|
|
5
|
+
import { getDefaultAiGatewayRunPromise } from '../ai-gateway/ai-gateway'
|
|
5
6
|
import type { ToolDefinition } from '../ai/definitions'
|
|
6
7
|
import { ERROR_TAGS } from '../effect/errors'
|
|
7
8
|
import type { makeAttachmentService } from '../services/attachment.service'
|
|
@@ -16,7 +17,7 @@ export interface ReadFilePartsToolContext {
|
|
|
16
17
|
userId: string
|
|
17
18
|
uploads: ReadableUploadMetadata[]
|
|
18
19
|
attachmentService: ReadFilePartsAttachmentService
|
|
19
|
-
runPromise
|
|
20
|
+
runPromise?: <A, E>(effect: Effect.Effect<A, E, never>, options?: { signal?: AbortSignal }) => Promise<A>
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
function toUploadSummary(upload: ReadableUploadMetadata) {
|
|
@@ -49,8 +50,9 @@ function resolveUploadTargetEffect(params: {
|
|
|
49
50
|
|
|
50
51
|
export const readFilePartsTool = {
|
|
51
52
|
name: 'readFileParts',
|
|
52
|
-
create: ({ orgId, userId, uploads, attachmentService, runPromise }: ReadFilePartsToolContext) =>
|
|
53
|
-
|
|
53
|
+
create: ({ orgId, userId, uploads, attachmentService, runPromise }: ReadFilePartsToolContext) => {
|
|
54
|
+
const runEffect = runPromise ?? getDefaultAiGatewayRunPromise()
|
|
55
|
+
return tool({
|
|
54
56
|
description:
|
|
55
57
|
'Read uploaded file content by part. Call with no args to list uploads metadata, then call with storageKey and part (25 pages per part).',
|
|
56
58
|
inputSchema: z
|
|
@@ -60,7 +62,7 @@ export const readFilePartsTool = {
|
|
|
60
62
|
{ storageKey, part }: { storageKey?: string; part?: number },
|
|
61
63
|
{ abortSignal }: { abortSignal?: AbortSignal } = {},
|
|
62
64
|
) =>
|
|
63
|
-
|
|
65
|
+
runEffect(
|
|
64
66
|
Effect.gen(function* () {
|
|
65
67
|
const availableUploads = uploads.map(toUploadSummary)
|
|
66
68
|
const selected = yield* resolveUploadTargetEffect({ uploads, storageKey })
|
|
@@ -93,5 +95,6 @@ export const readFilePartsTool = {
|
|
|
93
95
|
}),
|
|
94
96
|
abortSignal ? { signal: abortSignal } : undefined,
|
|
95
97
|
),
|
|
96
|
-
})
|
|
98
|
+
})
|
|
99
|
+
},
|
|
97
100
|
} as const satisfies ToolDefinition<ReadFilePartsToolContext>
|
|
@@ -2,6 +2,7 @@ import { tool } from 'ai'
|
|
|
2
2
|
import { Effect } from 'effect'
|
|
3
3
|
import { z } from 'zod'
|
|
4
4
|
|
|
5
|
+
import { getDefaultAiGatewayRunPromise } from '../ai-gateway/ai-gateway'
|
|
5
6
|
import type { RecordIdRef } from '../db/record-id'
|
|
6
7
|
import { recordIdToString } from '../db/record-id'
|
|
7
8
|
import { TABLES } from '../db/tables'
|
|
@@ -29,8 +30,9 @@ export function createRememberMemoryTool({
|
|
|
29
30
|
orgId: RecordIdRef
|
|
30
31
|
agentName: string
|
|
31
32
|
memoryService: RememberMemoryService
|
|
32
|
-
runPromise
|
|
33
|
+
runPromise?: <A, E>(effect: Effect.Effect<A, E, never>) => Promise<A>
|
|
33
34
|
}) {
|
|
35
|
+
const runEffect = runPromise ?? getDefaultAiGatewayRunPromise()
|
|
34
36
|
return tool({
|
|
35
37
|
description:
|
|
36
38
|
'Save a durable memory. Importance is scored automatically from memory significance. Use targetScope="agent" for role-specific memory, or targetScope="global" for organization-wide memory.',
|
|
@@ -44,7 +46,7 @@ export function createRememberMemoryTool({
|
|
|
44
46
|
const orgIdString = recordIdToString(orgId, TABLES.ORGANIZATION)
|
|
45
47
|
void safeEnqueue(
|
|
46
48
|
() =>
|
|
47
|
-
|
|
49
|
+
runEffect(
|
|
48
50
|
Effect.gen(function* () {
|
|
49
51
|
const assessment = yield* memoryService.assessMemoryCandidate({ orgId: orgIdString, content: trimmed })
|
|
50
52
|
if (!assessment) {
|
|
@@ -2,6 +2,7 @@ import type Firecrawl from '@mendable/firecrawl-js'
|
|
|
2
2
|
import type { Effect } from 'effect'
|
|
3
3
|
|
|
4
4
|
import type { AiGatewayModels } from '../ai-gateway/ai-gateway'
|
|
5
|
+
import { aiGatewayChatModel, getDefaultAiGatewayRunPromise } from '../ai-gateway/ai-gateway'
|
|
5
6
|
import { buildAiGatewayStrictSemanticCacheHeaders } from '../ai-gateway/cache-headers'
|
|
6
7
|
import {
|
|
7
8
|
OPENROUTER_FAST_REASONING_MODEL_ID,
|
|
@@ -14,15 +15,18 @@ import { searchWebTool } from './search-web.tool'
|
|
|
14
15
|
|
|
15
16
|
export interface ResearchTopicToolContext {
|
|
16
17
|
firecrawl: Firecrawl
|
|
17
|
-
aiGatewayModels
|
|
18
|
-
runPromise
|
|
18
|
+
aiGatewayModels?: AiGatewayModels
|
|
19
|
+
runPromise?: <A, E>(effect: Effect.Effect<A, E, never>, options?: { signal?: AbortSignal }) => Promise<A>
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export const researchTopicTool = createDelegatedAgentToolWithContext<ResearchTopicToolContext>({
|
|
22
23
|
id: 'researchTopic',
|
|
23
24
|
description:
|
|
24
25
|
'Delegate a research task to a dedicated research agent that searches the web, fetches pages, and returns a synthesized markdown report.',
|
|
25
|
-
model: ({ aiGatewayModels }) =>
|
|
26
|
+
model: ({ aiGatewayModels }) =>
|
|
27
|
+
aiGatewayModels
|
|
28
|
+
? aiGatewayModels.chatModel(OPENROUTER_FAST_REASONING_MODEL_ID)
|
|
29
|
+
: aiGatewayChatModel(OPENROUTER_FAST_REASONING_MODEL_ID),
|
|
26
30
|
providerOptions: OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS,
|
|
27
31
|
headers: buildAiGatewayStrictSemanticCacheHeaders('researchTopic'),
|
|
28
32
|
instructions: RESEARCHER_PROMPT,
|
|
@@ -31,5 +35,5 @@ export const researchTopicTool = createDelegatedAgentToolWithContext<ResearchTop
|
|
|
31
35
|
fetchWebpage: fetchWebpageTool.create({ firecrawl, runPromise }),
|
|
32
36
|
}),
|
|
33
37
|
maxSteps: 6,
|
|
34
|
-
getRunPromise: (context) => context.runPromise,
|
|
38
|
+
getRunPromise: (context) => context.runPromise ?? getDefaultAiGatewayRunPromise(),
|
|
35
39
|
})
|
|
@@ -3,6 +3,7 @@ import { tool } from 'ai'
|
|
|
3
3
|
import { Effect, Schema } from 'effect'
|
|
4
4
|
import { z } from 'zod'
|
|
5
5
|
|
|
6
|
+
import { getDefaultAiGatewayRunPromise } from '../ai-gateway/ai-gateway'
|
|
6
7
|
import type { ToolDefinition } from '../ai/definitions'
|
|
7
8
|
import { ERROR_TAGS } from '../effect/errors'
|
|
8
9
|
import { withTimeout } from '../utils/async'
|
|
@@ -13,7 +14,7 @@ import { toRecord, WEB_TOOL_TIMEOUT_MS } from './web-tool-shared'
|
|
|
13
14
|
|
|
14
15
|
export interface SearchWebToolContext {
|
|
15
16
|
firecrawl: Firecrawl
|
|
16
|
-
runPromise
|
|
17
|
+
runPromise?: <A, E>(effect: Effect.Effect<A, E, never>, options?: { signal?: AbortSignal }) => Promise<A>
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
class SearchWebToolError extends Schema.TaggedErrorClass<SearchWebToolError>()(ERROR_TAGS.SearchWebToolError, {
|
|
@@ -141,8 +142,9 @@ function buildWebCitations(results: { web?: unknown[]; news?: unknown[]; images?
|
|
|
141
142
|
|
|
142
143
|
export const searchWebTool = {
|
|
143
144
|
name: 'searchWeb',
|
|
144
|
-
create: ({ firecrawl, runPromise }: SearchWebToolContext) =>
|
|
145
|
-
|
|
145
|
+
create: ({ firecrawl, runPromise }: SearchWebToolContext) => {
|
|
146
|
+
const runEffect = runPromise ?? getDefaultAiGatewayRunPromise()
|
|
147
|
+
return tool({
|
|
146
148
|
description: 'Search the web for real-time information.',
|
|
147
149
|
inputSchema: z
|
|
148
150
|
.object({
|
|
@@ -163,7 +165,7 @@ export const searchWebTool = {
|
|
|
163
165
|
}: { query: string; limit?: number; sources?: ('web' | 'news' | 'images')[]; location?: string; tbs?: string },
|
|
164
166
|
{ abortSignal }: { abortSignal?: AbortSignal } = {},
|
|
165
167
|
) =>
|
|
166
|
-
|
|
168
|
+
runEffect(
|
|
167
169
|
Effect.gen(function* () {
|
|
168
170
|
const results = yield* Effect.tryPromise({
|
|
169
171
|
try: () =>
|
|
@@ -186,5 +188,6 @@ export const searchWebTool = {
|
|
|
186
188
|
}).pipe(Effect.withSpan('tool.searchWeb.execute')),
|
|
187
189
|
abortSignal ? { signal: abortSignal } : undefined,
|
|
188
190
|
),
|
|
189
|
-
})
|
|
191
|
+
})
|
|
192
|
+
},
|
|
190
193
|
} as const satisfies ToolDefinition<SearchWebToolContext>
|
package/src/tools/search.tool.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { tool } from 'ai'
|
|
|
2
2
|
import { Effect } from 'effect'
|
|
3
3
|
import { z } from 'zod'
|
|
4
4
|
|
|
5
|
+
import { getDefaultAiGatewayRunPromise } from '../ai-gateway/ai-gateway'
|
|
5
6
|
import type { ResolvedAgentConfig } from '../config/agent-defaults'
|
|
6
7
|
import { isAgentName } from '../config/agent-defaults'
|
|
7
8
|
import type { RecordIdRef } from '../db/record-id'
|
|
@@ -16,15 +17,33 @@ type MemorySearchService = Pick<ReturnType<typeof createMemoryService>, 'searchA
|
|
|
16
17
|
type ConversationSearchService = Pick<ReturnType<typeof makeThreadMessageService>, 'searchMessagesEffect'>
|
|
17
18
|
|
|
18
19
|
type SearchToolRunPromise = <A, E>(effect: Effect.Effect<A, E, never>, options?: { signal?: AbortSignal }) => Promise<A>
|
|
20
|
+
type MemorySearchOptions = { fastMode?: boolean; allowMultiScopeRerank?: boolean }
|
|
19
21
|
|
|
20
22
|
export function createMemorySearchTool(
|
|
21
23
|
agentConfig: ResolvedAgentConfig,
|
|
22
24
|
orgIdString: string,
|
|
23
25
|
memoryService: MemorySearchService,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
runPromiseOrAgentName?: SearchToolRunPromise | string,
|
|
27
|
+
agentNameOrOptions?: string | MemorySearchOptions,
|
|
28
|
+
optionsArg?: MemorySearchOptions,
|
|
27
29
|
) {
|
|
30
|
+
const runPromise =
|
|
31
|
+
typeof runPromiseOrAgentName === 'function' ? runPromiseOrAgentName : getDefaultAiGatewayRunPromise()
|
|
32
|
+
const agentName =
|
|
33
|
+
typeof runPromiseOrAgentName === 'string'
|
|
34
|
+
? runPromiseOrAgentName
|
|
35
|
+
: typeof agentNameOrOptions === 'string'
|
|
36
|
+
? agentNameOrOptions
|
|
37
|
+
: undefined
|
|
38
|
+
const options =
|
|
39
|
+
typeof runPromiseOrAgentName === 'function'
|
|
40
|
+
? typeof agentNameOrOptions === 'object'
|
|
41
|
+
? agentNameOrOptions
|
|
42
|
+
: optionsArg
|
|
43
|
+
: typeof agentNameOrOptions === 'object'
|
|
44
|
+
? agentNameOrOptions
|
|
45
|
+
: optionsArg
|
|
46
|
+
|
|
28
47
|
return tool({
|
|
29
48
|
description: 'Search organization and agent memories relevant to a query.',
|
|
30
49
|
inputSchema: MemorySearchInputSchema,
|
|
@@ -55,7 +74,7 @@ export function createMemorySearchTool(
|
|
|
55
74
|
export function createConversationSearchTool(
|
|
56
75
|
threadId: RecordIdRef,
|
|
57
76
|
threadMessageService: ConversationSearchService,
|
|
58
|
-
runPromise: SearchToolRunPromise,
|
|
77
|
+
runPromise: SearchToolRunPromise = getDefaultAiGatewayRunPromise(),
|
|
59
78
|
) {
|
|
60
79
|
return tool({
|
|
61
80
|
description: 'Search prior chat messages by role and query text.',
|
|
@@ -3,6 +3,7 @@ import { stepCountIs } from 'ai'
|
|
|
3
3
|
import type { ToolSet } from 'ai'
|
|
4
4
|
import { Schema, Effect } from 'effect'
|
|
5
5
|
|
|
6
|
+
import { getDefaultAiGatewayRunPromise } from '../ai-gateway/ai-gateway'
|
|
6
7
|
import { aiLogger } from '../config/logger'
|
|
7
8
|
import type { RecordIdRef } from '../db/record-id'
|
|
8
9
|
import { recordIdToString } from '../db/record-id'
|
|
@@ -72,8 +73,9 @@ export function createTeamThinkTool(params: {
|
|
|
72
73
|
context?: unknown
|
|
73
74
|
toolProviders?: ToolSet
|
|
74
75
|
abortSignal: AbortSignal
|
|
75
|
-
runPromise
|
|
76
|
+
runPromise?: <A, E>(effect: Effect.Effect<A, E, never>, options?: { signal?: AbortSignal }) => Promise<A>
|
|
76
77
|
}) {
|
|
78
|
+
const runEffect = params.runPromise ?? getDefaultAiGatewayRunPromise()
|
|
77
79
|
return Effect.gen(function* () {
|
|
78
80
|
const turnHooks = yield* TurnHooksServiceTag
|
|
79
81
|
const runtimeAdapters = yield* RuntimeAdaptersServiceTag
|
|
@@ -84,7 +86,7 @@ export function createTeamThinkTool(params: {
|
|
|
84
86
|
)
|
|
85
87
|
const participantRunner: TeamConsultationParticipantRunner = {
|
|
86
88
|
buildParticipantAgent(agentId, runParams) {
|
|
87
|
-
return
|
|
89
|
+
return runEffect(
|
|
88
90
|
Effect.gen(function* () {
|
|
89
91
|
// Capture the fiber context of the managed runtime and replay it
|
|
90
92
|
// for observer callbacks that cross the Effect → Promise boundary
|