@swarmclawai/swarmclaw 1.9.29 → 1.9.31
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/README.md +31 -0
- package/bin/swarmclaw.js +10 -5
- package/package.json +1 -1
- package/src/cli/binary.test.js +12 -3
- package/src/lib/server/connectors/email.test.ts +4 -1
- package/src/lib/server/connectors/email.ts +10 -2
- package/src/lib/server/memory/dream-generation-preference.ts +22 -0
- package/src/lib/server/memory/dream-service.test.ts +22 -0
- package/src/lib/server/memory/dream-service.ts +4 -1
- package/src/lib/server/memory/memory-consolidation.test.ts +37 -0
- package/src/lib/server/memory/memory-consolidation.ts +16 -5
- package/src/types/app-settings.ts +5 -0
package/README.md
CHANGED
|
@@ -151,6 +151,21 @@ clawhub install swarmclaw
|
|
|
151
151
|
|
|
152
152
|
[Browse on ClawHub](https://clawhub.ai/skills/swarmclaw)
|
|
153
153
|
|
|
154
|
+
## v1.9.31 Highlights
|
|
155
|
+
|
|
156
|
+
Documentation cleanup release for public release notes and OpenClaw guidance. No runtime behavior changed.
|
|
157
|
+
|
|
158
|
+
- **Public docs cleanup.** Removed an unwanted third-party example from the README and site release notes.
|
|
159
|
+
- **OpenClaw guidance preserved.** The README keeps the SwarmClaw-native OpenClaw gateway, skill, and agent-file guidance without naming unrelated workflows.
|
|
160
|
+
|
|
161
|
+
## v1.9.30 Highlights
|
|
162
|
+
|
|
163
|
+
PR integration release for dream-model routing, email bridge TLS opt-outs, and installed CLI runtime resolution.
|
|
164
|
+
|
|
165
|
+
- **Dream model routing.** Memory dream cycles and daily digests can use optional `dreamProvider` settings so background consolidation can run on a smaller local model.
|
|
166
|
+
- **Email bridge TLS opt-outs.** `tlsRejectUnauthorized=false` now disables hostname checks too, matching the explicit self-signed-server opt-out.
|
|
167
|
+
- **Installed CLI stability.** Legacy API-backed CLI commands import the package-local `tsx` runtime instead of resolving `tsx` from the caller's project.
|
|
168
|
+
|
|
154
169
|
## v1.9.29 Highlights
|
|
155
170
|
|
|
156
171
|
Issue-fix release for Edit Agent tooltips, installed package builds, and structured dream output on local Ollama models.
|
|
@@ -219,6 +234,7 @@ SwarmClaw is built for OpenClaw operators who need more than one agent or one ga
|
|
|
219
234
|
- Deploy official-image OpenClaw runtimes locally, via VPS bundles, or over SSH.
|
|
220
235
|
- Edit OpenClaw agent files such as `SOUL.md`, `IDENTITY.md`, `USER.md`, `TOOLS.md`, and `AGENTS.md`.
|
|
221
236
|
- Import OpenClaw `SKILL.md` files and use them in SwarmClaw's runtime skill system.
|
|
237
|
+
- Use OpenClaw plugins and skills through the configured gateway workflow without leaving the SwarmClaw control plane.
|
|
222
238
|
|
|
223
239
|
## Use Cases
|
|
224
240
|
|
|
@@ -410,6 +426,21 @@ Operational docs: https://swarmclaw.ai/docs/observability
|
|
|
410
426
|
|
|
411
427
|
## Releases
|
|
412
428
|
|
|
429
|
+
### v1.9.31 Highlights
|
|
430
|
+
|
|
431
|
+
Documentation cleanup release for public release notes and OpenClaw guidance. No runtime behavior changed.
|
|
432
|
+
|
|
433
|
+
- **Public docs cleanup.** Removed an unwanted third-party example from the README and site release notes.
|
|
434
|
+
- **OpenClaw guidance preserved.** The README keeps the SwarmClaw-native OpenClaw gateway, skill, and agent-file guidance without naming unrelated workflows.
|
|
435
|
+
|
|
436
|
+
### v1.9.30 Highlights
|
|
437
|
+
|
|
438
|
+
PR integration release for dream-model routing, email bridge TLS opt-outs, and installed CLI runtime resolution.
|
|
439
|
+
|
|
440
|
+
- **Dream model routing.** Memory dream cycles and daily digests can use optional `dreamProvider` settings so background consolidation can run on a smaller local model.
|
|
441
|
+
- **Email bridge TLS opt-outs.** `tlsRejectUnauthorized=false` now disables hostname checks too, matching the explicit self-signed-server opt-out.
|
|
442
|
+
- **Installed CLI stability.** Legacy API-backed CLI commands import the package-local `tsx` runtime instead of resolving `tsx` from the caller's project.
|
|
443
|
+
|
|
413
444
|
### v1.9.29 Highlights
|
|
414
445
|
|
|
415
446
|
Issue-fix release for Edit Agent tooltips, installed package builds, and structured dream output on local Ollama models.
|
package/bin/swarmclaw.js
CHANGED
|
@@ -47,11 +47,14 @@ function supportsStripTypes() {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function hasTsxRuntime() {
|
|
50
|
+
return Boolean(resolveTsxRuntimeImportPath())
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function resolveTsxRuntimeImportPath() {
|
|
50
54
|
try {
|
|
51
|
-
require.resolve('tsx
|
|
52
|
-
return true
|
|
55
|
+
return require.resolve('tsx')
|
|
53
56
|
} catch {
|
|
54
|
-
return
|
|
57
|
+
return null
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
60
|
|
|
@@ -71,9 +74,10 @@ function buildLegacyTsCliArgs(cliPath, argv, options = {}) {
|
|
|
71
74
|
return ['--no-warnings', '--experimental-strip-types', cliPath, ...argv]
|
|
72
75
|
}
|
|
73
76
|
|
|
74
|
-
const
|
|
77
|
+
const tsxImportPath = options.tsxImportPath ?? resolveTsxRuntimeImportPath()
|
|
78
|
+
const tsxAvailable = options.hasTsxRuntime ?? Boolean(tsxImportPath)
|
|
75
79
|
if (tsxAvailable) {
|
|
76
|
-
return ['--no-warnings', '--import', 'tsx', cliPath, ...argv]
|
|
80
|
+
return ['--no-warnings', '--import', tsxImportPath || 'tsx', cliPath, ...argv]
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
return null
|
|
@@ -374,6 +378,7 @@ module.exports = {
|
|
|
374
378
|
normalizeLegacyTsCliArgv,
|
|
375
379
|
pathIsInsideNodeModules,
|
|
376
380
|
resolveLegacyTsCliPath,
|
|
381
|
+
resolveTsxRuntimeImportPath,
|
|
377
382
|
TS_CLI_ACTIONS,
|
|
378
383
|
normalizeLegacyCliEnv,
|
|
379
384
|
printPackageVersion,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmclawai/swarmclaw",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.31",
|
|
4
4
|
"description": "Build and run autonomous AI agents with OpenClaw, Hermes, multiple model providers, orchestration, delegation, memory, skills, schedules, and chat connectors.",
|
|
5
5
|
"main": "electron-dist/main.js",
|
|
6
6
|
"license": "MIT",
|
package/src/cli/binary.test.js
CHANGED
|
@@ -7,7 +7,7 @@ const fs = require('node:fs')
|
|
|
7
7
|
const os = require('node:os')
|
|
8
8
|
const path = require('node:path')
|
|
9
9
|
const { spawnSync } = require('node:child_process')
|
|
10
|
-
const { buildLegacyTsCliArgs, resolveLegacyTsCliPath } = require('../../bin/swarmclaw.js')
|
|
10
|
+
const { buildLegacyTsCliArgs, resolveLegacyTsCliPath, resolveTsxRuntimeImportPath } = require('../../bin/swarmclaw.js')
|
|
11
11
|
|
|
12
12
|
const CLI_BIN = path.join(__dirname, '..', '..', 'bin', 'swarmclaw.js')
|
|
13
13
|
const PACKAGE_JSON = require('../../package.json')
|
|
@@ -207,21 +207,30 @@ test('package ships dagre type declarations required by installed builds', () =>
|
|
|
207
207
|
|
|
208
208
|
test('legacy TS launcher falls back to tsx import when strip-types is unavailable', () => {
|
|
209
209
|
const cliPath = path.join(APP_ROOT, 'src', 'cli', 'index.ts')
|
|
210
|
+
const tsxImportPath = resolveTsxRuntimeImportPath()
|
|
210
211
|
const args = buildLegacyTsCliArgs(cliPath, ['runs', 'list'], {
|
|
211
212
|
supportsStripTypes: false,
|
|
212
213
|
hasTsxRuntime: true,
|
|
213
214
|
})
|
|
214
215
|
|
|
215
|
-
assert.deepEqual(args, ['--no-warnings', '--import',
|
|
216
|
+
assert.deepEqual(args, ['--no-warnings', '--import', tsxImportPath, cliPath, 'runs', 'list'])
|
|
216
217
|
})
|
|
217
218
|
|
|
218
219
|
test('legacy TS launcher uses tsx instead of strip-types inside node_modules', () => {
|
|
219
220
|
const cliPath = path.join(os.tmpdir(), 'node_modules', '@swarmclawai', 'swarmclaw', 'src', 'cli', 'index.ts')
|
|
221
|
+
const tsxImportPath = resolveTsxRuntimeImportPath()
|
|
220
222
|
const args = buildLegacyTsCliArgs(cliPath, ['agents', 'list'], {
|
|
221
223
|
supportsStripTypes: true,
|
|
222
224
|
hasTsxRuntime: true,
|
|
223
225
|
})
|
|
224
226
|
|
|
225
|
-
assert.deepEqual(args, ['--no-warnings', '--import',
|
|
227
|
+
assert.deepEqual(args, ['--no-warnings', '--import', tsxImportPath, cliPath, 'agents', 'list'])
|
|
226
228
|
assert.equal(resolveLegacyTsCliPath(), path.join(APP_ROOT, 'src', 'cli', 'index.ts'))
|
|
227
229
|
})
|
|
230
|
+
|
|
231
|
+
test('legacy TS launcher imports the package-local tsx runtime by absolute path', () => {
|
|
232
|
+
const tsxImportPath = resolveTsxRuntimeImportPath()
|
|
233
|
+
|
|
234
|
+
assert.equal(path.isAbsolute(tsxImportPath), true)
|
|
235
|
+
assert.match(tsxImportPath, /tsx/)
|
|
236
|
+
})
|
|
@@ -81,7 +81,10 @@ describe('email TLS configuration', () => {
|
|
|
81
81
|
assert.equal(parseTlsRejectUnauthorized(false), false)
|
|
82
82
|
assert.equal(parseTlsRejectUnauthorized('false'), false)
|
|
83
83
|
assert.equal(parseTlsRejectUnauthorized('0'), false)
|
|
84
|
-
|
|
84
|
+
const tls = buildEmailTlsOptions({ tlsRejectUnauthorized: false })
|
|
85
|
+
assert.equal(tls.rejectUnauthorized, false)
|
|
86
|
+
assert.equal(typeof tls.checkServerIdentity, 'function')
|
|
87
|
+
assert.equal(tls.checkServerIdentity?.('localhost', {} as never), undefined)
|
|
85
88
|
})
|
|
86
89
|
|
|
87
90
|
it('handles IMAP socket errors without leaving the emitter unhandled', () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import path from 'path'
|
|
3
|
+
import type { ConnectionOptions } from 'node:tls'
|
|
3
4
|
import { ImapFlow } from 'imapflow'
|
|
4
5
|
import { createTransport, type Transporter } from 'nodemailer'
|
|
5
6
|
import { simpleParser } from 'mailparser'
|
|
@@ -34,6 +35,10 @@ interface ImapErrorEmitter {
|
|
|
34
35
|
on(event: 'error', listener: (err: unknown) => void): unknown
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
type EmailTlsOptions = Pick<ConnectionOptions, 'rejectUnauthorized' | 'checkServerIdentity'> & {
|
|
39
|
+
rejectUnauthorized: boolean
|
|
40
|
+
}
|
|
41
|
+
|
|
37
42
|
export function buildAttachments(options?: OutboundSendOptions): MailAttachment[] {
|
|
38
43
|
const source = options?.mediaPath
|
|
39
44
|
if (!source) return []
|
|
@@ -59,8 +64,11 @@ export function parseTlsRejectUnauthorized(value: unknown): boolean {
|
|
|
59
64
|
return true
|
|
60
65
|
}
|
|
61
66
|
|
|
62
|
-
export function buildEmailTlsOptions(config: Pick<EmailConfig, 'tlsRejectUnauthorized'>):
|
|
63
|
-
|
|
67
|
+
export function buildEmailTlsOptions(config: Pick<EmailConfig, 'tlsRejectUnauthorized'>): EmailTlsOptions {
|
|
68
|
+
const reject = config.tlsRejectUnauthorized !== false
|
|
69
|
+
return reject
|
|
70
|
+
? { rejectUnauthorized: true }
|
|
71
|
+
: { rejectUnauthorized: false, checkServerIdentity: () => undefined }
|
|
64
72
|
}
|
|
65
73
|
|
|
66
74
|
export function attachImapErrorHandler(imap: ImapErrorEmitter, onDisconnected: () => void): void {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { GenerationModelPreference } from '@/lib/server/build-llm'
|
|
2
|
+
import type { AppSettings } from '@/types'
|
|
3
|
+
|
|
4
|
+
type DreamGenerationSettings = Pick<AppSettings, 'dreamProvider' | 'dreamModel' | 'dreamCredentialId' | 'dreamEndpoint'> | Record<string, unknown> | null | undefined
|
|
5
|
+
|
|
6
|
+
function optionalSettingString(value: unknown): string | undefined {
|
|
7
|
+
const normalized = typeof value === 'string' ? value.trim() : ''
|
|
8
|
+
return normalized || undefined
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function resolveDreamGenerationPreference(settings: DreamGenerationSettings): GenerationModelPreference | undefined {
|
|
12
|
+
const record = (settings || {}) as Record<string, unknown>
|
|
13
|
+
const provider = optionalSettingString(record.dreamProvider)
|
|
14
|
+
if (!provider) return undefined
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
provider,
|
|
18
|
+
model: optionalSettingString(record.dreamModel),
|
|
19
|
+
credentialId: optionalSettingString(record.dreamCredentialId),
|
|
20
|
+
apiEndpoint: optionalSettingString(record.dreamEndpoint),
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
import assert from 'node:assert/strict'
|
|
2
2
|
import { describe, it } from 'node:test'
|
|
3
|
+
import { resolveDreamGenerationPreference } from './dream-generation-preference'
|
|
3
4
|
import { parseTier2DreamResponseText } from './dream-service'
|
|
4
5
|
|
|
6
|
+
describe('resolveDreamGenerationPreference', () => {
|
|
7
|
+
it('returns no preference when no dream provider is configured', () => {
|
|
8
|
+
assert.equal(resolveDreamGenerationPreference({}), undefined)
|
|
9
|
+
assert.equal(resolveDreamGenerationPreference({ dreamProvider: ' ' }), undefined)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('builds a trimmed dream model preference from app settings', () => {
|
|
13
|
+
assert.deepEqual(resolveDreamGenerationPreference({
|
|
14
|
+
dreamProvider: ' ollama ',
|
|
15
|
+
dreamModel: ' gemma4:e4b ',
|
|
16
|
+
dreamCredentialId: ' cred-1 ',
|
|
17
|
+
dreamEndpoint: ' http://localhost:11434 ',
|
|
18
|
+
}), {
|
|
19
|
+
provider: 'ollama',
|
|
20
|
+
model: 'gemma4:e4b',
|
|
21
|
+
credentialId: 'cred-1',
|
|
22
|
+
apiEndpoint: 'http://localhost:11434',
|
|
23
|
+
})
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
|
|
5
27
|
describe('parseTier2DreamResponseText', () => {
|
|
6
28
|
it('parses a plain structured dream response', () => {
|
|
7
29
|
const parsed = parseTier2DreamResponseText(JSON.stringify({
|
|
@@ -6,6 +6,7 @@ import { getMemoryDb } from '@/lib/server/memory/memory-db'
|
|
|
6
6
|
import { saveDreamCycle } from '@/lib/server/memory/dream-cycles'
|
|
7
7
|
import { errorMessage } from '@/lib/shared-utils'
|
|
8
8
|
import { log } from '@/lib/server/logger'
|
|
9
|
+
import { resolveDreamGenerationPreference } from '@/lib/server/memory/dream-generation-preference'
|
|
9
10
|
|
|
10
11
|
const TAG = 'dream-service'
|
|
11
12
|
|
|
@@ -214,7 +215,9 @@ ${memoryLines.join('\n')}`
|
|
|
214
215
|
|
|
215
216
|
try {
|
|
216
217
|
const { buildLLM } = await import('@/lib/server/build-llm')
|
|
217
|
-
const {
|
|
218
|
+
const { loadSettings } = await import('@/lib/server/settings/settings-repository')
|
|
219
|
+
const preferred = resolveDreamGenerationPreference(loadSettings())
|
|
220
|
+
const { llm } = await buildLLM({ agentId, preferred, responseFormat: 'json_object' })
|
|
218
221
|
const { HumanMessage } = await import('@langchain/core/messages')
|
|
219
222
|
|
|
220
223
|
const response = await llm.invoke([new HumanMessage(prompt)])
|
|
@@ -38,6 +38,7 @@ after(() => {
|
|
|
38
38
|
})
|
|
39
39
|
|
|
40
40
|
test('runDailyConsolidation skips orphaned and CLI-only agent namespaces without reporting errors', async () => {
|
|
41
|
+
storage.saveSettings({})
|
|
41
42
|
const db = memDb.getMemoryDb()
|
|
42
43
|
const now = Date.now()
|
|
43
44
|
const orphanId = 'live-orphan-agent'
|
|
@@ -87,3 +88,39 @@ test('runDailyConsolidation skips orphaned and CLI-only agent namespaces without
|
|
|
87
88
|
false,
|
|
88
89
|
)
|
|
89
90
|
})
|
|
91
|
+
|
|
92
|
+
test('canCreateDailyDigestForAgent allows CLI-only agents when a dream model is configured', async () => {
|
|
93
|
+
const now = Date.now()
|
|
94
|
+
const agentId = 'dream-routed-cli-agent'
|
|
95
|
+
storage.saveAgents({
|
|
96
|
+
[agentId]: {
|
|
97
|
+
id: agentId,
|
|
98
|
+
name: 'Dream Routed CLI Agent',
|
|
99
|
+
description: '',
|
|
100
|
+
systemPrompt: '',
|
|
101
|
+
provider: 'claude-cli',
|
|
102
|
+
model: 'claude-sonnet-4-5',
|
|
103
|
+
credentialId: null,
|
|
104
|
+
fallbackCredentialIds: [],
|
|
105
|
+
apiEndpoint: null,
|
|
106
|
+
createdAt: now,
|
|
107
|
+
updatedAt: now,
|
|
108
|
+
} as Agent,
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
storage.saveSettings({})
|
|
112
|
+
assert.equal(
|
|
113
|
+
consolidation.canCreateDailyDigestForAgent(agentId, storage.loadAgents({ includeTrashed: true }), storage.loadSettings()),
|
|
114
|
+
false,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
storage.saveSettings({
|
|
118
|
+
dreamProvider: 'ollama',
|
|
119
|
+
dreamModel: 'llama3.2',
|
|
120
|
+
dreamEndpoint: 'http://127.0.0.1:11434',
|
|
121
|
+
})
|
|
122
|
+
assert.equal(
|
|
123
|
+
consolidation.canCreateDailyDigestForAgent(agentId, storage.loadAgents({ includeTrashed: true }), storage.loadSettings()),
|
|
124
|
+
true,
|
|
125
|
+
)
|
|
126
|
+
})
|
|
@@ -4,6 +4,9 @@ import { resolveGenerationModelConfig } from '@/lib/server/build-llm'
|
|
|
4
4
|
import { HumanMessage } from '@langchain/core/messages'
|
|
5
5
|
import { errorMessage } from '@/lib/shared-utils'
|
|
6
6
|
import { onNextIdleWindow } from '@/lib/server/runtime/idle-window'
|
|
7
|
+
import { loadSettings } from '@/lib/server/settings/settings-repository'
|
|
8
|
+
import { resolveDreamGenerationPreference } from '@/lib/server/memory/dream-generation-preference'
|
|
9
|
+
import type { AppSettings } from '@/types'
|
|
7
10
|
|
|
8
11
|
let consolidationRegistered = false
|
|
9
12
|
let compactionRegistered = false
|
|
@@ -35,14 +38,18 @@ export function registerCompactionIdleCallback(): void {
|
|
|
35
38
|
})
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
function canCreateDailyDigestForAgent(
|
|
41
|
+
export function canCreateDailyDigestForAgent(
|
|
39
42
|
agentId: string,
|
|
40
43
|
agents: ReturnType<typeof loadAgents>,
|
|
44
|
+
settings: Partial<AppSettings> | Record<string, unknown> | null | undefined = loadSettings(),
|
|
41
45
|
): boolean {
|
|
42
46
|
const agent = agents[agentId]
|
|
43
47
|
if (!agent || agent.trashedAt) return false
|
|
44
48
|
try {
|
|
45
|
-
resolveGenerationModelConfig({
|
|
49
|
+
resolveGenerationModelConfig({
|
|
50
|
+
agentId,
|
|
51
|
+
preferred: resolveDreamGenerationPreference(settings),
|
|
52
|
+
})
|
|
46
53
|
return true
|
|
47
54
|
} catch (err: unknown) {
|
|
48
55
|
const message = errorMessage(err)
|
|
@@ -65,6 +72,7 @@ export async function runDailyConsolidation(): Promise<{
|
|
|
65
72
|
const memDb = getMemoryDb()
|
|
66
73
|
const counts = memDb.countsByAgent()
|
|
67
74
|
const agents = loadAgents({ includeTrashed: true })
|
|
75
|
+
const settings = loadSettings()
|
|
68
76
|
const today = new Date().toISOString().slice(0, 10) // YYYY-MM-DD
|
|
69
77
|
const digestTitle = `Daily digest: ${today}`
|
|
70
78
|
const cutoff24h = Date.now() - 24 * 3600_000
|
|
@@ -76,7 +84,7 @@ export async function runDailyConsolidation(): Promise<{
|
|
|
76
84
|
const agentId = agentKey
|
|
77
85
|
|
|
78
86
|
try {
|
|
79
|
-
if (!canCreateDailyDigestForAgent(agentId, agents)) continue
|
|
87
|
+
if (!canCreateDailyDigestForAgent(agentId, agents, settings)) continue
|
|
80
88
|
|
|
81
89
|
// Check if digest already exists for today
|
|
82
90
|
const existing = memDb.search(digestTitle, agentId)
|
|
@@ -109,9 +117,12 @@ export async function runDailyConsolidation(): Promise<{
|
|
|
109
117
|
...memoryLines,
|
|
110
118
|
].join('\n')
|
|
111
119
|
|
|
112
|
-
// Use the target agent's
|
|
120
|
+
// Use an optional dream-model override before the target agent's generation provider.
|
|
113
121
|
const { buildLLM } = await import('@/lib/server/build-llm')
|
|
114
|
-
const { llm } = await buildLLM({
|
|
122
|
+
const { llm } = await buildLLM({
|
|
123
|
+
agentId,
|
|
124
|
+
preferred: resolveDreamGenerationPreference(settings),
|
|
125
|
+
})
|
|
115
126
|
|
|
116
127
|
const response = await llm.invoke([new HumanMessage(prompt)])
|
|
117
128
|
const digestContent = typeof response.content === 'string'
|
|
@@ -27,6 +27,11 @@ export interface AppSettings {
|
|
|
27
27
|
embeddingModel?: string | null
|
|
28
28
|
embeddingCredentialId?: string | null
|
|
29
29
|
embeddingEndpoint?: string | null
|
|
30
|
+
// Optional model override for memory consolidation and dream cycles.
|
|
31
|
+
dreamProvider?: string | null
|
|
32
|
+
dreamModel?: string | null
|
|
33
|
+
dreamCredentialId?: string | null
|
|
34
|
+
dreamEndpoint?: string | null
|
|
30
35
|
loopMode?: LoopMode
|
|
31
36
|
agentLoopRecursionLimit?: number
|
|
32
37
|
delegationMaxDepth?: number
|