@swarmclawai/swarmclaw 1.9.29 → 1.9.30
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 +19 -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,15 @@ clawhub install swarmclaw
|
|
|
151
151
|
|
|
152
152
|
[Browse on ClawHub](https://clawhub.ai/skills/swarmclaw)
|
|
153
153
|
|
|
154
|
+
## v1.9.30 Highlights
|
|
155
|
+
|
|
156
|
+
PR integration release for dream-model routing, email bridge TLS opt-outs, installed CLI runtime resolution, and an OpenClaw plugin workflow example.
|
|
157
|
+
|
|
158
|
+
- **Dream model routing.** Memory dream cycles and daily digests can use optional `dreamProvider` settings so background consolidation can run on a smaller local model.
|
|
159
|
+
- **Email bridge TLS opt-outs.** `tlsRejectUnauthorized=false` now disables hostname checks too, matching the explicit self-signed-server opt-out.
|
|
160
|
+
- **Installed CLI stability.** Legacy API-backed CLI commands import the package-local `tsx` runtime instead of resolving `tsx` from the caller's project.
|
|
161
|
+
- **OpenClaw plugin workflow.** README guidance now includes a concrete TweetClaw plugin workflow for OpenClaw operators.
|
|
162
|
+
|
|
154
163
|
## v1.9.29 Highlights
|
|
155
164
|
|
|
156
165
|
Issue-fix release for Edit Agent tooltips, installed package builds, and structured dream output on local Ollama models.
|
|
@@ -219,6 +228,7 @@ SwarmClaw is built for OpenClaw operators who need more than one agent or one ga
|
|
|
219
228
|
- Deploy official-image OpenClaw runtimes locally, via VPS bundles, or over SSH.
|
|
220
229
|
- Edit OpenClaw agent files such as `SOUL.md`, `IDENTITY.md`, `USER.md`, `TOOLS.md`, and `AGENTS.md`.
|
|
221
230
|
- Import OpenClaw `SKILL.md` files and use them in SwarmClaw's runtime skill system.
|
|
231
|
+
- Use OpenClaw plugins for domain workflows. For example, `openclaw plugins install @xquik/tweetclaw` installs [TweetClaw](https://github.com/Xquik-dev/tweetclaw) via [ClawHub](https://clawhub.ai/kriptoburak/xquik-tweetclaw) for X/Twitter search, follower export, monitors, webhooks, and approval-gated post/reply actions.
|
|
222
232
|
|
|
223
233
|
## Use Cases
|
|
224
234
|
|
|
@@ -410,6 +420,15 @@ Operational docs: https://swarmclaw.ai/docs/observability
|
|
|
410
420
|
|
|
411
421
|
## Releases
|
|
412
422
|
|
|
423
|
+
### v1.9.30 Highlights
|
|
424
|
+
|
|
425
|
+
PR integration release for dream-model routing, email bridge TLS opt-outs, installed CLI runtime resolution, and an OpenClaw plugin workflow example.
|
|
426
|
+
|
|
427
|
+
- **Dream model routing.** Memory dream cycles and daily digests can use optional `dreamProvider` settings so background consolidation can run on a smaller local model.
|
|
428
|
+
- **Email bridge TLS opt-outs.** `tlsRejectUnauthorized=false` now disables hostname checks too, matching the explicit self-signed-server opt-out.
|
|
429
|
+
- **Installed CLI stability.** Legacy API-backed CLI commands import the package-local `tsx` runtime instead of resolving `tsx` from the caller's project.
|
|
430
|
+
- **OpenClaw plugin workflow.** README guidance now includes a concrete TweetClaw plugin workflow for OpenClaw operators.
|
|
431
|
+
|
|
413
432
|
### v1.9.29 Highlights
|
|
414
433
|
|
|
415
434
|
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.30",
|
|
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
|