@lota-sdk/core 0.4.4 → 0.4.6
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 +0 -11
- package/src/config/background-processing.ts +0 -9
- package/src/config/model-constants.ts +0 -1
- package/src/create-runtime.ts +0 -2
- package/src/db/service.ts +7 -25
- package/src/queues/autonomous-job.queue.ts +0 -4
- package/src/runtime/agent-runtime-policy.ts +1 -1
- package/src/services/artifact.service.ts +5 -7
- package/src/services/autonomous-job.service.ts +8 -12
- package/src/services/plan-agent-query.service.ts +4 -3
- package/src/services/plan-builder.service.ts +1 -6
- package/src/services/plan-deadline.service.ts +8 -9
- package/src/services/plan-scheduler.service.ts +4 -4
- package/src/system-agents/agent-result.ts +9 -0
- package/src/tools/execution-plan.tool.ts +18 -1
- package/src/workers/worker-utils.ts +0 -68
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lota-sdk/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@chat-adapter/slack": "^4.23.0",
|
|
33
33
|
"@chat-adapter/state-ioredis": "^4.23.0",
|
|
34
34
|
"@logtape/logtape": "^2.0.5",
|
|
35
|
-
"@lota-sdk/shared": "0.4.
|
|
35
|
+
"@lota-sdk/shared": "0.4.6",
|
|
36
36
|
"@mendable/firecrawl-js": "^4.18.1",
|
|
37
37
|
"@surrealdb/node": "^3.0.3",
|
|
38
38
|
"ai": "^6.0.145",
|
|
@@ -305,17 +305,6 @@ export function injectAiGatewayExtraParamsRequestBody(
|
|
|
305
305
|
return JSON.stringify({ ...parsed, extra_params: mergedExtraParams })
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
export function injectAiGatewayOpenAIPromptCacheRetentionRequestBody(
|
|
309
|
-
body: BodyInit | null | undefined,
|
|
310
|
-
): BodyInit | null | undefined {
|
|
311
|
-
const parsed = parseAiGatewayJsonRequestBody(body)
|
|
312
|
-
if (!parsed) return body
|
|
313
|
-
if (!readString(parsed.model)?.startsWith('openai/')) return body
|
|
314
|
-
if (readString(parsed.prompt_cache_retention) !== null) return body
|
|
315
|
-
|
|
316
|
-
return JSON.stringify({ ...parsed, prompt_cache_retention: OPENAI_PROMPT_CACHE_RETENTION })
|
|
317
|
-
}
|
|
318
|
-
|
|
319
308
|
function createAiGatewayFetch(extraParams?: AiGatewayExtraParams): typeof fetch {
|
|
320
309
|
const fetchWithMutations = (input: RequestInfo | URL, init?: RequestInit | BunFetchRequestInit) => {
|
|
321
310
|
const parsedBody = parseAiGatewayJsonRequestBody(init?.body)
|
|
@@ -14,15 +14,6 @@ const DEFAULT_CONFIG: BackgroundProcessingConfig = {
|
|
|
14
14
|
|
|
15
15
|
let resolvedConfig: BackgroundProcessingConfig = { ...DEFAULT_CONFIG }
|
|
16
16
|
|
|
17
|
-
export function configureBackgroundProcessing(config?: Partial<BackgroundProcessingConfig>): void {
|
|
18
|
-
resolvedConfig = {
|
|
19
|
-
memoryExtractionFrequency: config?.memoryExtractionFrequency ?? DEFAULT_CONFIG.memoryExtractionFrequency,
|
|
20
|
-
skillExtractionFrequency: config?.skillExtractionFrequency ?? DEFAULT_CONFIG.skillExtractionFrequency,
|
|
21
|
-
memoryDigestFrequency: config?.memoryDigestFrequency ?? DEFAULT_CONFIG.memoryDigestFrequency,
|
|
22
|
-
memoryConsolidationFrequency: config?.memoryConsolidationFrequency ?? DEFAULT_CONFIG.memoryConsolidationFrequency,
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
17
|
export function getBackgroundProcessingConfig(): BackgroundProcessingConfig {
|
|
27
18
|
return resolvedConfig
|
|
28
19
|
}
|
|
@@ -8,7 +8,6 @@ export {
|
|
|
8
8
|
OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS,
|
|
9
9
|
OPENROUTER_MEDIUM_REASONING_PROVIDER_OPTIONS,
|
|
10
10
|
OPENROUTER_MINIMAL_REASONING_PROVIDER_OPTIONS,
|
|
11
|
-
OPENROUTER_STRUCTURED_REASONING_MODEL_ID,
|
|
12
11
|
OPENROUTER_TEAM_AGENT_MODEL_ID,
|
|
13
12
|
OPENROUTER_WEB_RESEARCH_MODEL_ID,
|
|
14
13
|
OPENROUTER_XHIGH_REASONING_PROVIDER_OPTIONS,
|
package/src/create-runtime.ts
CHANGED
|
@@ -2,7 +2,6 @@ import type { ChatMessage } from '@lota-sdk/shared'
|
|
|
2
2
|
|
|
3
3
|
import { configureEmbeddingCache } from './ai/embedding-cache'
|
|
4
4
|
import { configureAgentFactory, configureAgents } from './config/agent-defaults'
|
|
5
|
-
import { configureBackgroundProcessing } from './config/background-processing'
|
|
6
5
|
import { configureLotaLogger } from './config/logger'
|
|
7
6
|
import { configureThreads } from './config/thread-defaults'
|
|
8
7
|
import { ensureRecordId } from './db/record-id'
|
|
@@ -251,7 +250,6 @@ export async function createLotaRuntime(config: LotaRuntimeConfig): Promise<Lota
|
|
|
251
250
|
const redisManager = createRedisConnectionManager({ url: runtimeConfig.redis.url })
|
|
252
251
|
setRedisConnectionManager(redisManager)
|
|
253
252
|
configureEmbeddingCache(redisManager.getConnection(), runtimeConfig.memory.embeddingCacheTtlSeconds)
|
|
254
|
-
configureBackgroundProcessing()
|
|
255
253
|
configureSocialChatHistory({ keyPrefix: runtimeConfig.socialChat?.historyRedisKeyPrefix })
|
|
256
254
|
|
|
257
255
|
const socialChatAgentId = runtimeConfig.socialChat?.agentId?.trim() || 'socialChat'
|
package/src/db/service.ts
CHANGED
|
@@ -44,8 +44,6 @@ export interface SurrealDatabaseLogger {
|
|
|
44
44
|
error?: (message: string) => void
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
export type BoundQueryLike = { query: string; bindings?: Record<string, unknown> }
|
|
48
|
-
|
|
49
47
|
interface FindManyOptions {
|
|
50
48
|
limit?: number
|
|
51
49
|
offset?: number
|
|
@@ -91,18 +89,6 @@ function configureMutation(
|
|
|
91
89
|
return builder.merge(data)
|
|
92
90
|
}
|
|
93
91
|
|
|
94
|
-
function isBoundQueryLike(value: unknown): value is BoundQueryLike {
|
|
95
|
-
if (!isRecord(value)) {
|
|
96
|
-
return false
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (typeof value.query !== 'string') {
|
|
100
|
-
return false
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return value.bindings === undefined || isRecord(value.bindings)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
92
|
const CONNECT_MAX_ATTEMPTS = 5
|
|
107
93
|
const CONNECT_RETRY_BASE_DELAY_MS = 100
|
|
108
94
|
const CONNECT_RETRY_JITTER_MS = 50
|
|
@@ -396,11 +382,7 @@ export class SurrealDBService {
|
|
|
396
382
|
return { clause: clauses.join(' AND '), bindings }
|
|
397
383
|
}
|
|
398
384
|
|
|
399
|
-
private normalizeBoundQuery(query: BoundQuery
|
|
400
|
-
if (!(query instanceof BoundQuery) && !isBoundQueryLike(query)) {
|
|
401
|
-
throw new SurrealDBError('Invalid query object: expected a BoundQuery-like value')
|
|
402
|
-
}
|
|
403
|
-
|
|
385
|
+
private normalizeBoundQuery(query: BoundQuery): BoundQuery {
|
|
404
386
|
return new BoundQuery(query.query, this.normalizeBindings(query.bindings))
|
|
405
387
|
}
|
|
406
388
|
|
|
@@ -533,7 +515,7 @@ export class SurrealDBService {
|
|
|
533
515
|
private wrapTransaction(tx: SurrealTransaction): DatabaseTransaction {
|
|
534
516
|
return {
|
|
535
517
|
query: async (query: unknown) => {
|
|
536
|
-
const boundQuery = this.normalizeBoundQuery(query as BoundQuery
|
|
518
|
+
const boundQuery = this.normalizeBoundQuery(query as BoundQuery)
|
|
537
519
|
const queryText = this.resolveQueryText(boundQuery)
|
|
538
520
|
|
|
539
521
|
try {
|
|
@@ -578,16 +560,16 @@ export class SurrealDBService {
|
|
|
578
560
|
}
|
|
579
561
|
}
|
|
580
562
|
|
|
581
|
-
private resolveQueryText(query: BoundQuery
|
|
563
|
+
private resolveQueryText(query: BoundQuery): string {
|
|
582
564
|
return query.query
|
|
583
565
|
}
|
|
584
566
|
|
|
585
|
-
async query<T>(query: BoundQuery
|
|
567
|
+
async query<T>(query: BoundQuery): Promise<T[]> {
|
|
586
568
|
const statements = await this.queryAll<T>(query)
|
|
587
569
|
return statements.at(0) ?? []
|
|
588
570
|
}
|
|
589
571
|
|
|
590
|
-
async queryAll<T>(query: BoundQuery
|
|
572
|
+
async queryAll<T>(query: BoundQuery, schema?: z.ZodTypeAny): Promise<T[][]> {
|
|
591
573
|
const client = await this.ensureConnected()
|
|
592
574
|
const boundQuery = this.normalizeBoundQuery(query)
|
|
593
575
|
const queryText = this.resolveQueryText(boundQuery)
|
|
@@ -607,13 +589,13 @@ export class SurrealDBService {
|
|
|
607
589
|
}
|
|
608
590
|
}
|
|
609
591
|
|
|
610
|
-
async queryOne<T extends z.ZodTypeAny>(query: BoundQuery
|
|
592
|
+
async queryOne<T extends z.ZodTypeAny>(query: BoundQuery, schema: T): Promise<z.infer<T> | null> {
|
|
611
593
|
const results = await this.query<unknown>(query)
|
|
612
594
|
const first = results.at(0)
|
|
613
595
|
return first ? this.parseSchema(schema, first) : null
|
|
614
596
|
}
|
|
615
597
|
|
|
616
|
-
async queryMany<T extends z.ZodTypeAny>(query: BoundQuery
|
|
598
|
+
async queryMany<T extends z.ZodTypeAny>(query: BoundQuery, schema: T): Promise<z.infer<T>[]> {
|
|
617
599
|
const results = await this.query<unknown>(query)
|
|
618
600
|
return results.map((row) => this.parseSchema(schema, row))
|
|
619
601
|
}
|
|
@@ -129,10 +129,6 @@ export function startAutonomousJobWorker(options: AutonomousJobWorkerOptions = {
|
|
|
129
129
|
return handle
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
export function getAutonomousJobQueueHandle(): WorkerHandle {
|
|
133
|
-
return startAutonomousJobWorker()
|
|
134
|
-
}
|
|
135
|
-
|
|
136
132
|
if (import.meta.main) {
|
|
137
133
|
startAutonomousJobWorker()
|
|
138
134
|
}
|
|
@@ -183,7 +183,7 @@ export function buildThreadAgentToolPolicy<TAgent extends string, TSkill extends
|
|
|
183
183
|
includeMemoryRemember: !params.onboardingActive,
|
|
184
184
|
includeOrgActionSearch: !params.onboardingActive,
|
|
185
185
|
includeMemoryBlockAppend: true,
|
|
186
|
-
includeReadFileParts:
|
|
186
|
+
includeReadFileParts: true,
|
|
187
187
|
includeExecutionPlanTools: !params.onboardingActive,
|
|
188
188
|
includeInspectWebsite: params.onboardingActive && params.agentId === onboardingOwnerAgentId,
|
|
189
189
|
includeProceedInOnboarding: params.onboardingActive && params.agentId === onboardingOwnerAgentId,
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
GetArtifactResult,
|
|
8
8
|
PublishArtifactArgs,
|
|
9
9
|
} from '@lota-sdk/shared'
|
|
10
|
+
import { BoundQuery } from 'surrealdb'
|
|
10
11
|
|
|
11
12
|
import type { RecordIdInput } from '../db/record-id'
|
|
12
13
|
import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
@@ -361,13 +362,10 @@ class ArtifactService {
|
|
|
361
362
|
async listBacklinks(artifact: Pick<ArtifactRecord, 'id' | 'organizationId'>): Promise<ArtifactRecord[]> {
|
|
362
363
|
const artifactId = recordIdToString(artifact.id, TABLES.ARTIFACT)
|
|
363
364
|
const records = await databaseService.queryMany(
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
targetId: artifactId,
|
|
369
|
-
},
|
|
370
|
-
},
|
|
365
|
+
new BoundQuery(
|
|
366
|
+
`SELECT * FROM ${TABLES.ARTIFACT} WHERE organizationId = $organizationId AND references[*].targetId CONTAINS $targetId`,
|
|
367
|
+
{ organizationId: ensureRecordId(artifact.organizationId, TABLES.ORGANIZATION), targetId: artifactId },
|
|
368
|
+
),
|
|
371
369
|
ArtifactRecordSchema,
|
|
372
370
|
)
|
|
373
371
|
return records
|
|
@@ -22,7 +22,7 @@ import type {
|
|
|
22
22
|
} from '@lota-sdk/shared'
|
|
23
23
|
import type { Job } from 'bullmq'
|
|
24
24
|
import { CronExpressionParser } from 'cron-parser'
|
|
25
|
-
import { RecordId } from 'surrealdb'
|
|
25
|
+
import { BoundQuery, RecordId } from 'surrealdb'
|
|
26
26
|
import { z } from 'zod'
|
|
27
27
|
|
|
28
28
|
import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
@@ -262,17 +262,14 @@ class AutonomousJobService {
|
|
|
262
262
|
|
|
263
263
|
private async findRecoverableRunRow(autonomousJobId: RecordIdInput): Promise<AutonomousJobRunRow | null> {
|
|
264
264
|
const rows = await databaseService.queryMany(
|
|
265
|
-
|
|
266
|
-
|
|
265
|
+
new BoundQuery(
|
|
266
|
+
`SELECT * FROM ${TABLES.AUTONOMOUS_JOB_RUN}
|
|
267
267
|
WHERE autonomousJobId = $autonomousJobId
|
|
268
268
|
AND status IN $statuses
|
|
269
269
|
ORDER BY createdAt DESC
|
|
270
270
|
LIMIT 1`,
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
statuses: ['queued', 'running'],
|
|
274
|
-
},
|
|
275
|
-
},
|
|
271
|
+
{ autonomousJobId: ensureRecordId(autonomousJobId, TABLES.AUTONOMOUS_JOB), statuses: ['queued', 'running'] },
|
|
272
|
+
),
|
|
276
273
|
AutonomousJobRunRowSchema,
|
|
277
274
|
)
|
|
278
275
|
|
|
@@ -371,10 +368,9 @@ class AutonomousJobService {
|
|
|
371
368
|
async recoverActiveJobs(now = new Date()): Promise<void> {
|
|
372
369
|
await databaseService.connect()
|
|
373
370
|
const activeRows = await databaseService.queryMany(
|
|
374
|
-
{
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
},
|
|
371
|
+
new BoundQuery(`SELECT * FROM ${TABLES.AUTONOMOUS_JOB} WHERE status = $status ORDER BY createdAt ASC`, {
|
|
372
|
+
status: 'active',
|
|
373
|
+
}),
|
|
378
374
|
AutonomousJobRowSchema,
|
|
379
375
|
)
|
|
380
376
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PlanExecutionVisibility, PlanNodeSpecRecord, PlanRunRecord, PlanSpecRecord } from '@lota-sdk/shared'
|
|
2
2
|
import { PlanRunSchema } from '@lota-sdk/shared'
|
|
3
|
+
import { BoundQuery } from 'surrealdb'
|
|
3
4
|
|
|
4
5
|
import type { RecordIdInput } from '../db/record-id'
|
|
5
6
|
import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
@@ -254,10 +255,10 @@ class PlanAgentQueryService {
|
|
|
254
255
|
|
|
255
256
|
const whereOrganization = organizationId ? ' AND organizationId = $organizationId' : ''
|
|
256
257
|
return databaseService.queryMany(
|
|
257
|
-
|
|
258
|
-
|
|
258
|
+
new BoundQuery(
|
|
259
|
+
`SELECT * FROM ${TABLES.PLAN_RUN} WHERE status INSIDE $statuses${whereOrganization} ORDER BY updatedAt DESC`,
|
|
259
260
|
bindings,
|
|
260
|
-
|
|
261
|
+
),
|
|
261
262
|
PlanRunSchema,
|
|
262
263
|
)
|
|
263
264
|
}
|
|
@@ -18,10 +18,6 @@ function buildImplicitLinearEdges(draft: PlanDraft) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
class PlanBuilderService {
|
|
21
|
-
roleAssignment(draft: PlanDraft): PlanDraft {
|
|
22
|
-
return draft
|
|
23
|
-
}
|
|
24
|
-
|
|
25
21
|
structureDesign(draft: PlanDraft): PlanDraft {
|
|
26
22
|
return {
|
|
27
23
|
...draft,
|
|
@@ -63,8 +59,7 @@ class PlanBuilderService {
|
|
|
63
59
|
}
|
|
64
60
|
|
|
65
61
|
prepareDraft(draft: PlanDraft): PlanDraft {
|
|
66
|
-
const
|
|
67
|
-
const withStructure = this.structureDesign(withRoles)
|
|
62
|
+
const withStructure = this.structureDesign(draft)
|
|
68
63
|
return this.semanticCompletion(withStructure)
|
|
69
64
|
}
|
|
70
65
|
}
|
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
PlanRunRecord,
|
|
9
9
|
} from '@lota-sdk/shared'
|
|
10
10
|
import { PlanEventSchema, PlanNodeRunSchema, PlanNodeSpecRecordSchema, PlanRunSchema } from '@lota-sdk/shared'
|
|
11
|
-
import { RecordId } from 'surrealdb'
|
|
11
|
+
import { BoundQuery, RecordId } from 'surrealdb'
|
|
12
12
|
|
|
13
13
|
import type { RecordIdInput } from '../db/record-id'
|
|
14
14
|
import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
@@ -185,10 +185,9 @@ class PlanDeadlineService {
|
|
|
185
185
|
entries: Array<{ nodeRun: PlanNodeRunRecord; nodeSpec: PlanNodeSpecRecord; evaluation: DeadlineEvaluationResult }>
|
|
186
186
|
}> {
|
|
187
187
|
const activeNodeRuns = await databaseService.queryMany(
|
|
188
|
-
{
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
},
|
|
188
|
+
new BoundQuery(`SELECT * FROM ${TABLES.PLAN_NODE_RUN} WHERE status IN $statuses`, {
|
|
189
|
+
statuses: ['running', 'awaiting-human'],
|
|
190
|
+
}),
|
|
192
191
|
PlanNodeRunSchema,
|
|
193
192
|
)
|
|
194
193
|
|
|
@@ -380,20 +379,20 @@ class PlanDeadlineService {
|
|
|
380
379
|
|
|
381
380
|
if (dedupeKey) {
|
|
382
381
|
const existing = await databaseService.queryMany(
|
|
383
|
-
|
|
384
|
-
|
|
382
|
+
new BoundQuery(
|
|
383
|
+
`SELECT * FROM ${TABLES.PLAN_EVENT}
|
|
385
384
|
WHERE runId = $runId
|
|
386
385
|
AND nodeId = $nodeId
|
|
387
386
|
AND eventType = $eventType
|
|
388
387
|
AND detail.dedupeKey = $dedupeKey
|
|
389
388
|
LIMIT 1`,
|
|
390
|
-
|
|
389
|
+
{
|
|
391
390
|
runId: ensureRecordId(params.run.id, TABLES.PLAN_RUN),
|
|
392
391
|
nodeId: params.nodeRun.nodeId,
|
|
393
392
|
eventType: params.eventType,
|
|
394
393
|
dedupeKey,
|
|
395
394
|
},
|
|
396
|
-
|
|
395
|
+
),
|
|
397
396
|
PlanEventSchema,
|
|
398
397
|
)
|
|
399
398
|
if (existing.length > 0) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PlanCycleRecordSchema, PlanScheduleRecordSchema } from '@lota-sdk/shared'
|
|
2
2
|
import type { PlanScheduleRecord, PlanScheduleSpec } from '@lota-sdk/shared'
|
|
3
3
|
import { CronExpressionParser } from 'cron-parser'
|
|
4
|
+
import { BoundQuery } from 'surrealdb'
|
|
4
5
|
|
|
5
6
|
import type { RecordIdInput } from '../db/record-id'
|
|
6
7
|
import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
@@ -156,10 +157,9 @@ class PlanSchedulerService {
|
|
|
156
157
|
/** Re-enqueue BullMQ jobs for all active schedules. Called once at worker startup. */
|
|
157
158
|
async recoverActiveSchedules(): Promise<void> {
|
|
158
159
|
const activeSchedules = await databaseService.queryMany(
|
|
159
|
-
{
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
},
|
|
160
|
+
new BoundQuery(`SELECT * FROM ${TABLES.PLAN_SCHEDULE} WHERE status = $status ORDER BY nextFireAt ASC`, {
|
|
161
|
+
status: 'active',
|
|
162
|
+
}),
|
|
163
163
|
PlanScheduleRecordSchema,
|
|
164
164
|
)
|
|
165
165
|
|
|
@@ -25,3 +25,12 @@ export function assertSubstantiveAgentResult(value: string, label = 'Agent resul
|
|
|
25
25
|
|
|
26
26
|
return normalized
|
|
27
27
|
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Returns the normalized text if substantive, otherwise returns the fallback.
|
|
31
|
+
* Useful for delegated agent results where the agent may end on a tool call without prose output.
|
|
32
|
+
*/
|
|
33
|
+
export function resolveSubstantiveAgentResult(value: string, fallback: string): string {
|
|
34
|
+
const normalized = normalizeAgentResultText(value)
|
|
35
|
+
return isSubstantiveAgentResult(normalized) ? normalized : fallback
|
|
36
|
+
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
ExecutionPlanQueryArgsSchema,
|
|
6
6
|
ExecutionPlanReplaceArgsSchema,
|
|
7
7
|
ExecutionPlanResumeArgsSchema,
|
|
8
|
+
ExecutionPlanUpdateNodeArgsSchema,
|
|
8
9
|
SubmitExecutionNodeResultArgsSchema,
|
|
9
10
|
AgentPlanDraftSchema,
|
|
10
11
|
expandAgentPlanDraft,
|
|
@@ -26,6 +27,7 @@ type ExecutionPlanExecutionPlanService = Pick<
|
|
|
26
27
|
| 'createPlan'
|
|
27
28
|
| 'replacePlan'
|
|
28
29
|
| 'resumeRun'
|
|
30
|
+
| 'submitNodeResult'
|
|
29
31
|
| 'listActivePlanSummaries'
|
|
30
32
|
| 'getActivePlanToolResult'
|
|
31
33
|
| 'getActivePlansForThread'
|
|
@@ -46,7 +48,7 @@ export function createExecutionPlanTool(params: {
|
|
|
46
48
|
|
|
47
49
|
return tool({
|
|
48
50
|
description:
|
|
49
|
-
'Manage execution plans. Actions: create (inline, 1-2 nodes), create-project (dedicated project thread, 3+ nodes), replace (swap active plan), resume (resume interrupted plan).',
|
|
51
|
+
'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).',
|
|
50
52
|
inputSchema: ExecutionPlanArgsSchema,
|
|
51
53
|
execute: async (input) => {
|
|
52
54
|
const parsed = parseExecutionPlanArgs(input)
|
|
@@ -148,6 +150,19 @@ export function createExecutionPlanTool(params: {
|
|
|
148
150
|
input: { runId: parsed.runId },
|
|
149
151
|
})
|
|
150
152
|
break
|
|
153
|
+
|
|
154
|
+
case 'update-node':
|
|
155
|
+
result = await resolvedEpService.submitNodeResult({
|
|
156
|
+
threadId: params.threadId,
|
|
157
|
+
emittedBy: params.agentId,
|
|
158
|
+
input: {
|
|
159
|
+
runId: parsed.runId,
|
|
160
|
+
nodeId: parsed.node.id,
|
|
161
|
+
notes: parsed.node.latestNotes,
|
|
162
|
+
artifacts: parsed.node.deliverables ?? [],
|
|
163
|
+
},
|
|
164
|
+
})
|
|
165
|
+
break
|
|
151
166
|
}
|
|
152
167
|
|
|
153
168
|
params.onPlanChanged?.()
|
|
@@ -168,6 +183,8 @@ function parseExecutionPlanArgs(input: unknown): ExecutionPlanArgs {
|
|
|
168
183
|
return ExecutionPlanReplaceArgsSchema.parse(parsed)
|
|
169
184
|
case 'resume':
|
|
170
185
|
return ExecutionPlanResumeArgsSchema.parse(parsed)
|
|
186
|
+
case 'update-node':
|
|
187
|
+
return ExecutionPlanUpdateNodeArgsSchema.parse(parsed)
|
|
171
188
|
}
|
|
172
189
|
}
|
|
173
190
|
|
|
@@ -5,17 +5,11 @@ import type { Job, Worker } from 'bullmq'
|
|
|
5
5
|
|
|
6
6
|
import { chatLogger } from '../config/logger'
|
|
7
7
|
import { queueJobService } from '../services/queue-job.service'
|
|
8
|
-
import { truncateText } from '../utils/string'
|
|
9
|
-
|
|
10
8
|
export const DEFAULT_JOB_RETENTION = { removeOnComplete: true, removeOnFail: { age: 24 * 60 * 60, count: 200 } }
|
|
11
9
|
export const LOW_JOB_RETENTION = { removeOnComplete: true, removeOnFail: { age: 6 * 60 * 60, count: 50 } }
|
|
12
10
|
export const LONG_JOB_LOCK_DURATION_MS = 600_000
|
|
13
11
|
|
|
14
12
|
const DEFAULT_SHUTDOWN_TIMEOUT_MS = 10_000
|
|
15
|
-
const MAX_TRACE_STRING_CHARS = 2_000
|
|
16
|
-
const MAX_TRACE_ARRAY_ITEMS = 12
|
|
17
|
-
const MAX_TRACE_OBJECT_KEYS = 24
|
|
18
|
-
const MAX_TRACE_DEPTH = 4
|
|
19
13
|
|
|
20
14
|
export function getWorkerPath(workerName: string): string {
|
|
21
15
|
return fileURLToPath(new URL(path.join('.', workerName), import.meta.url))
|
|
@@ -35,68 +29,6 @@ interface TracedWorkerJobLike {
|
|
|
35
29
|
timestamp?: number
|
|
36
30
|
}
|
|
37
31
|
|
|
38
|
-
function truncateTraceString(value: string, maxChars = MAX_TRACE_STRING_CHARS): string {
|
|
39
|
-
return truncateText(value, maxChars)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function normalizeTraceValue(value: unknown, depth = 0): unknown {
|
|
43
|
-
if (value === null || value === undefined) return value
|
|
44
|
-
if (typeof value === 'string') return truncateTraceString(value)
|
|
45
|
-
if (typeof value === 'number' || typeof value === 'boolean') return value
|
|
46
|
-
if (typeof value === 'bigint') return value.toString()
|
|
47
|
-
if (typeof value === 'symbol') return value.description ? `Symbol(${value.description})` : 'Symbol()'
|
|
48
|
-
if (typeof value === 'function') return value.name ? `[function ${value.name}]` : '[function anonymous]'
|
|
49
|
-
if (value instanceof Date) return value.toISOString()
|
|
50
|
-
|
|
51
|
-
if (depth >= MAX_TRACE_DEPTH) {
|
|
52
|
-
if (Array.isArray(value)) return `[array(${value.length})]`
|
|
53
|
-
return '[object]'
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (Array.isArray(value)) {
|
|
57
|
-
return value.slice(0, MAX_TRACE_ARRAY_ITEMS).map((item) => normalizeTraceValue(item, depth + 1))
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (!(value instanceof Date) && typeof value === 'object') {
|
|
61
|
-
const record = value as Record<string, unknown>
|
|
62
|
-
return Object.fromEntries(
|
|
63
|
-
Object.entries(record)
|
|
64
|
-
.slice(0, MAX_TRACE_OBJECT_KEYS)
|
|
65
|
-
.map(([key, entryValue]) => [key, normalizeTraceValue(entryValue, depth + 1)]),
|
|
66
|
-
)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return '[unknown]'
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function serializeTraceValue(value: unknown): string {
|
|
73
|
-
const serialized = JSON.stringify(normalizeTraceValue(value))
|
|
74
|
-
return truncateTraceString(serialized || 'null')
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function traceTextValue(value: unknown): string {
|
|
78
|
-
if (typeof value === 'string') return truncateTraceString(value)
|
|
79
|
-
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
80
|
-
return truncateTraceString(String(value))
|
|
81
|
-
}
|
|
82
|
-
if (value instanceof Date) return value.toISOString()
|
|
83
|
-
return serializeTraceValue(value)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function buildWorkerObservationMetadata(
|
|
87
|
-
queueName: string,
|
|
88
|
-
job: { id?: unknown; name: string; attemptsMade: number | null | undefined },
|
|
89
|
-
): Record<string, string> {
|
|
90
|
-
return {
|
|
91
|
-
queue: traceTextValue(queueName),
|
|
92
|
-
job_name: traceTextValue(job.name),
|
|
93
|
-
...(job.id !== undefined ? { job_id: traceTextValue(job.id) } : {}),
|
|
94
|
-
...(job.attemptsMade !== null && job.attemptsMade !== undefined
|
|
95
|
-
? { attempts_made: traceTextValue(job.attemptsMade) }
|
|
96
|
-
: {}),
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
32
|
export const attachWorkerEvents = (worker: Worker, name: string, logger: typeof chatLogger = chatLogger) => {
|
|
101
33
|
worker.on('ready', () => {
|
|
102
34
|
logger.info`${name} worker ready`
|