@swarmclawai/swarmclaw 1.2.1 → 1.2.3
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 +16 -85
- package/bin/server-cmd.js +64 -1
- package/package.json +2 -2
- package/skills/coding-agent/SKILL.md +111 -0
- package/skills/github/SKILL.md +140 -0
- package/skills/nano-banana-pro/SKILL.md +62 -0
- package/skills/nano-banana-pro/scripts/generate_image.py +235 -0
- package/skills/nano-pdf/SKILL.md +53 -0
- package/skills/openai-image-gen/SKILL.md +78 -0
- package/skills/openai-image-gen/scripts/gen.py +328 -0
- package/skills/resourceful-problem-solving/SKILL.md +49 -0
- package/skills/skill-creator/SKILL.md +147 -0
- package/skills/skill-creator/scripts/init_skill.py +378 -0
- package/skills/skill-creator/scripts/quick_validate.py +159 -0
- package/skills/summarize/SKILL.md +77 -0
- package/src/app/api/auth/route.ts +20 -5
- package/src/app/api/chats/[id]/devserver/route.ts +13 -19
- package/src/app/api/chats/[id]/messages/route.ts +13 -15
- package/src/app/api/chats/[id]/route.ts +9 -10
- package/src/app/api/chats/[id]/stop/route.ts +5 -7
- package/src/app/api/chats/messages-route.test.ts +8 -6
- package/src/app/api/chats/route.ts +9 -10
- package/src/app/api/ip/route.ts +2 -2
- package/src/app/api/preview-server/route.ts +1 -1
- package/src/app/api/projects/[id]/route.ts +7 -46
- package/src/cli/server-cmd.test.js +74 -0
- package/src/components/chat/chat-area.tsx +45 -23
- package/src/components/chat/message-bubble.test.ts +35 -0
- package/src/components/chat/message-bubble.tsx +19 -9
- package/src/components/chat/message-list.tsx +37 -3
- package/src/components/input/chat-input.tsx +34 -14
- package/src/components/openclaw/openclaw-deploy-panel.tsx +4 -0
- package/src/instrumentation.ts +1 -1
- package/src/lib/chat/assistant-render-id.ts +3 -0
- package/src/lib/chat/chat-streaming-state.test.ts +42 -3
- package/src/lib/chat/chat-streaming-state.ts +20 -8
- package/src/lib/chat/queued-message-queue.test.ts +23 -1
- package/src/lib/chat/queued-message-queue.ts +11 -2
- package/src/lib/providers/cli-utils.test.ts +124 -0
- package/src/lib/server/activity/activity-log.ts +21 -0
- package/src/lib/server/agents/agent-availability.test.ts +10 -5
- package/src/lib/server/agents/agent-cascade.ts +79 -59
- package/src/lib/server/agents/agent-registry.ts +3 -1
- package/src/lib/server/agents/agent-repository.ts +90 -0
- package/src/lib/server/agents/delegation-job-repository.ts +53 -0
- package/src/lib/server/agents/delegation-jobs.ts +11 -4
- package/src/lib/server/agents/guardian-checkpoint-repository.ts +35 -0
- package/src/lib/server/agents/guardian.ts +2 -2
- package/src/lib/server/agents/main-agent-loop.ts +10 -3
- package/src/lib/server/agents/main-loop-state-repository.ts +38 -0
- package/src/lib/server/agents/subagent-runtime.ts +9 -6
- package/src/lib/server/agents/subagent-swarm.ts +3 -2
- package/src/lib/server/agents/task-session.ts +3 -4
- package/src/lib/server/approvals/approval-repository.ts +30 -0
- package/src/lib/server/autonomy/supervisor-incident-repository.ts +42 -0
- package/src/lib/server/chat-execution/chat-execution-types.ts +38 -0
- package/src/lib/server/chat-execution/chat-execution-utils.ts +1 -1
- package/src/lib/server/chat-execution/chat-execution.ts +84 -1926
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +620 -0
- package/src/lib/server/chat-execution/chat-turn-partial-persistence.ts +221 -0
- package/src/lib/server/chat-execution/chat-turn-preflight.ts +133 -0
- package/src/lib/server/chat-execution/chat-turn-preparation.ts +817 -0
- package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +296 -0
- package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +5 -5
- package/src/lib/server/chat-execution/message-classifier.test.ts +329 -0
- package/src/lib/server/chat-execution/post-stream-finalization.ts +1 -1
- package/src/lib/server/chat-execution/prompt-builder.ts +11 -0
- package/src/lib/server/chat-execution/prompt-sections.ts +5 -6
- package/src/lib/server/chat-execution/situational-awareness.ts +12 -7
- package/src/lib/server/chat-execution/stream-agent-chat.ts +16 -13
- package/src/lib/server/chatrooms/chatroom-repository.ts +32 -0
- package/src/lib/server/connectors/connector-repository.ts +58 -0
- package/src/lib/server/connectors/runtime-state.test.ts +117 -0
- package/src/lib/server/credentials/credential-repository.ts +7 -0
- package/src/lib/server/gateways/gateway-profile-repository.ts +4 -0
- package/src/lib/server/memory/memory-abstract.test.ts +59 -0
- package/src/lib/server/missions/mission-repository.ts +74 -0
- package/src/lib/server/missions/mission-service/actions.ts +6 -0
- package/src/lib/server/missions/mission-service/bindings.ts +9 -0
- package/src/lib/server/missions/mission-service/context.ts +4 -0
- package/src/lib/server/missions/mission-service/core.ts +2269 -0
- package/src/lib/server/missions/mission-service/queries.ts +12 -0
- package/src/lib/server/missions/mission-service/recovery.ts +5 -0
- package/src/lib/server/missions/mission-service/ticks.ts +9 -0
- package/src/lib/server/missions/mission-service.test.ts +9 -2
- package/src/lib/server/missions/mission-service.ts +6 -2266
- package/src/lib/server/openclaw/deploy.test.ts +42 -3
- package/src/lib/server/openclaw/deploy.ts +26 -12
- package/src/lib/server/persistence/repository-utils.ts +154 -0
- package/src/lib/server/persistence/storage-context.ts +51 -0
- package/src/lib/server/persistence/transaction.ts +1 -0
- package/src/lib/server/projects/project-repository.ts +36 -0
- package/src/lib/server/projects/project-service.ts +79 -0
- package/src/lib/server/protocols/protocol-normalization.test.ts +6 -4
- package/src/lib/server/runtime/alert-dispatch.ts +1 -1
- package/src/lib/server/runtime/daemon-policy.ts +1 -1
- package/src/lib/server/runtime/daemon-state/core.ts +1570 -0
- package/src/lib/server/runtime/daemon-state/health.ts +6 -0
- package/src/lib/server/runtime/daemon-state/policy.ts +7 -0
- package/src/lib/server/runtime/daemon-state/supervisor.ts +6 -0
- package/src/lib/server/runtime/daemon-state.test.ts +48 -0
- package/src/lib/server/runtime/daemon-state.ts +3 -1470
- package/src/lib/server/runtime/estop-repository.ts +4 -0
- package/src/lib/server/runtime/estop.ts +3 -1
- package/src/lib/server/runtime/heartbeat-service.test.ts +2 -2
- package/src/lib/server/runtime/heartbeat-service.ts +55 -34
- package/src/lib/server/runtime/heartbeat-wake.ts +6 -4
- package/src/lib/server/runtime/idle-window.ts +2 -2
- package/src/lib/server/runtime/network.ts +11 -0
- package/src/lib/server/runtime/orchestrator-events.ts +2 -2
- package/src/lib/server/runtime/queue/claims.ts +4 -0
- package/src/lib/server/runtime/queue/core.ts +2079 -0
- package/src/lib/server/runtime/queue/execution.ts +7 -0
- package/src/lib/server/runtime/queue/followups.ts +4 -0
- package/src/lib/server/runtime/queue/queries.ts +12 -0
- package/src/lib/server/runtime/queue/recovery.ts +7 -0
- package/src/lib/server/runtime/queue-recovery.test.ts +48 -13
- package/src/lib/server/runtime/queue-repository.ts +17 -0
- package/src/lib/server/runtime/queue.ts +5 -2061
- package/src/lib/server/runtime/run-ledger.ts +6 -5
- package/src/lib/server/runtime/run-repository.ts +73 -0
- package/src/lib/server/runtime/runtime-lock-repository.ts +8 -0
- package/src/lib/server/runtime/runtime-settings.ts +1 -1
- package/src/lib/server/runtime/runtime-state.ts +99 -0
- package/src/lib/server/runtime/scheduler.ts +4 -2
- package/src/lib/server/runtime/session-run-manager/cancellation.ts +157 -0
- package/src/lib/server/runtime/session-run-manager/drain.ts +246 -0
- package/src/lib/server/runtime/session-run-manager/enqueue.ts +287 -0
- package/src/lib/server/runtime/session-run-manager/queries.ts +117 -0
- package/src/lib/server/runtime/session-run-manager/recovery.ts +238 -0
- package/src/lib/server/runtime/session-run-manager/state.ts +441 -0
- package/src/lib/server/runtime/session-run-manager/types.ts +74 -0
- package/src/lib/server/runtime/session-run-manager.ts +72 -1377
- package/src/lib/server/runtime/watch-job-repository.ts +35 -0
- package/src/lib/server/runtime/watch-jobs.ts +3 -1
- package/src/lib/server/schedules/schedule-repository.ts +42 -0
- package/src/lib/server/sessions/session-repository.ts +85 -0
- package/src/lib/server/settings/settings-repository.ts +25 -0
- package/src/lib/server/skills/skill-discovery.test.ts +2 -2
- package/src/lib/server/skills/skill-discovery.ts +2 -2
- package/src/lib/server/skills/skill-repository.ts +14 -0
- package/src/lib/server/storage.ts +13 -24
- package/src/lib/server/tasks/task-repository.ts +54 -0
- package/src/lib/server/usage/usage-repository.ts +30 -0
- package/src/lib/server/webhooks/webhook-repository.ts +10 -0
- package/src/lib/strip-internal-metadata.test.ts +42 -41
- package/src/stores/use-chat-store.test.ts +54 -0
- package/src/stores/use-chat-store.ts +21 -5
- /package/{bundled-skills → skills}/google-workspace/SKILL.md +0 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ApprovalRequest, EstopState } from '@/types'
|
|
2
|
-
import { loadApprovals,
|
|
2
|
+
import { loadApprovals, upsertApproval } from '@/lib/server/approvals/approval-repository'
|
|
3
|
+
import { loadPersistedEstopState, savePersistedEstopState } from '@/lib/server/runtime/estop-repository'
|
|
4
|
+
import { loadSettings } from '@/lib/server/settings/settings-repository'
|
|
3
5
|
import { requestApproval } from '@/lib/server/approvals'
|
|
4
6
|
|
|
5
7
|
const DEFAULT_ESTOP_STATE: EstopState = {
|
|
@@ -370,8 +370,8 @@ describe('buildAgentHeartbeatPrompt', () => {
|
|
|
370
370
|
{
|
|
371
371
|
id: 's1',
|
|
372
372
|
messages: [
|
|
373
|
-
{ role: 'user', text: 'Check the logs', toolEvents: [] },
|
|
374
|
-
{ role: 'assistant', text: 'Logs look clean', toolEvents: [] },
|
|
373
|
+
{ role: 'user', text: 'Check the logs', toolEvents: [], time: Date.now() },
|
|
374
|
+
{ role: 'assistant', text: 'Logs look clean', toolEvents: [], time: Date.now() },
|
|
375
375
|
],
|
|
376
376
|
},
|
|
377
377
|
{ name: 'Bot' },
|
|
@@ -6,7 +6,13 @@ import {
|
|
|
6
6
|
DEFAULT_HEARTBEAT_SHOW_ALERTS,
|
|
7
7
|
DEFAULT_HEARTBEAT_SHOW_OK,
|
|
8
8
|
} from '@/lib/runtime/heartbeat-defaults'
|
|
9
|
-
import {
|
|
9
|
+
import { logActivity } from '@/lib/server/activity/activity-log'
|
|
10
|
+
import { loadApprovals } from '@/lib/server/approvals/approval-repository'
|
|
11
|
+
import { loadAgents, patchAgent } from '@/lib/server/agents/agent-repository'
|
|
12
|
+
import { loadChatrooms } from '@/lib/server/chatrooms/chatroom-repository'
|
|
13
|
+
import { loadMission } from '@/lib/server/missions/mission-repository'
|
|
14
|
+
import { loadSessions, patchSession } from '@/lib/server/sessions/session-repository'
|
|
15
|
+
import { loadSettings } from '@/lib/server/settings/settings-repository'
|
|
10
16
|
import { buildGoalAncestrySection, buildPlatformStatusSummary } from '@/lib/server/chat-execution/situational-awareness'
|
|
11
17
|
import { drainDeferredWakes, hasDeferredWakes } from '@/lib/server/runtime/wake-dispatcher'
|
|
12
18
|
import { buildWakeTriggerContext } from '@/lib/server/runtime/heartbeat-wake'
|
|
@@ -15,7 +21,7 @@ import { log } from '@/lib/server/logger'
|
|
|
15
21
|
import { WORKSPACE_DIR } from '@/lib/server/data-dir'
|
|
16
22
|
import { drainSystemEvents, drainOrchestratorEvents } from '@/lib/server/runtime/system-events'
|
|
17
23
|
import { buildMissionContextBlock } from '@/lib/server/missions/mission-service'
|
|
18
|
-
import type { Agent, Chatroom } from '@/types'
|
|
24
|
+
import type { Agent, AppSettings, ApprovalRequest, Chatroom, Message, Session } from '@/types'
|
|
19
25
|
import { isOrchestratorEligible } from '@/lib/orchestrator-config'
|
|
20
26
|
import { buildIdentityContinuityContext } from '@/lib/server/identity-continuity'
|
|
21
27
|
import { buildMainLoopHeartbeatPrompt, getMainLoopStateForSession, isMainSession } from '@/lib/server/agents/main-agent-loop'
|
|
@@ -23,7 +29,6 @@ import { ensureAgentThreadSession } from '@/lib/server/agents/agent-thread-sessi
|
|
|
23
29
|
import { isAgentDisabled } from '@/lib/server/agents/agent-availability'
|
|
24
30
|
import { errorMessage, hmrSingleton, jitteredBackoff } from '@/lib/shared-utils'
|
|
25
31
|
import { logExecution } from '@/lib/server/execution-log'
|
|
26
|
-
import { logActivity } from '@/lib/server/storage'
|
|
27
32
|
import { createNotification } from '@/lib/server/create-notification'
|
|
28
33
|
import { WORKER_ONLY_PROVIDER_IDS } from '@/lib/provider-sets'
|
|
29
34
|
|
|
@@ -190,6 +195,10 @@ interface HeartbeatFileSession {
|
|
|
190
195
|
cwd?: string | null
|
|
191
196
|
}
|
|
192
197
|
|
|
198
|
+
type HeartbeatPromptSession = Partial<Session> & Record<string, unknown>
|
|
199
|
+
type HeartbeatPromptAgent = Partial<Agent>
|
|
200
|
+
type HeartbeatPromptMessage = Pick<Message, 'role' | 'text' | 'time' | 'toolEvents'>
|
|
201
|
+
|
|
193
202
|
const DEFAULT_HEARTBEAT_PROMPT = 'Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK.'
|
|
194
203
|
|
|
195
204
|
export function readHeartbeatFile(session: HeartbeatFileSession): string {
|
|
@@ -202,18 +211,24 @@ export function readHeartbeatFile(session: HeartbeatFileSession): string {
|
|
|
202
211
|
return ''
|
|
203
212
|
}
|
|
204
213
|
|
|
205
|
-
const identityFileCache = hmrSingleton<Map<string, { data: Record<string, string>; expiresAt: number }>>(
|
|
214
|
+
const identityFileCache = hmrSingleton<Map<string, { data: Record<string, string>; expiresAt: number; mtimeMs: number | null }>>(
|
|
206
215
|
'__hb_identity_cache__', () => new Map(),
|
|
207
216
|
)
|
|
208
217
|
const IDENTITY_CACHE_TTL_MS = 60_000
|
|
209
218
|
|
|
210
219
|
function readIdentityFile(session: { cwd?: string | null }): Record<string, string> {
|
|
211
220
|
const cwd = typeof session.cwd === 'string' ? session.cwd : WORKSPACE_DIR
|
|
221
|
+
const filePath = path.join(cwd, 'IDENTITY.md')
|
|
222
|
+
let mtimeMs: number | null = null
|
|
223
|
+
try {
|
|
224
|
+
mtimeMs = fs.statSync(filePath).mtimeMs
|
|
225
|
+
} catch {
|
|
226
|
+
mtimeMs = null
|
|
227
|
+
}
|
|
212
228
|
const cached = identityFileCache.get(cwd)
|
|
213
|
-
if (cached && Date.now() < cached.expiresAt) return cached.data
|
|
229
|
+
if (cached && Date.now() < cached.expiresAt && cached.mtimeMs === mtimeMs) return cached.data
|
|
214
230
|
try {
|
|
215
|
-
|
|
216
|
-
if (fs.existsSync(filePath)) {
|
|
231
|
+
if (mtimeMs !== null) {
|
|
217
232
|
const content = fs.readFileSync(filePath, 'utf-8')
|
|
218
233
|
const identity: Record<string, string> = {}
|
|
219
234
|
for (const line of content.split('\n')) {
|
|
@@ -224,12 +239,12 @@ function readIdentityFile(session: { cwd?: string | null }): Record<string, stri
|
|
|
224
239
|
const value = cleaned.slice(colonIndex + 1).replace(/^[*_]+|[*_]+$/g, '').trim()
|
|
225
240
|
if (value) identity[label] = value
|
|
226
241
|
}
|
|
227
|
-
identityFileCache.set(cwd, { data: identity, expiresAt: Date.now() + IDENTITY_CACHE_TTL_MS })
|
|
242
|
+
identityFileCache.set(cwd, { data: identity, expiresAt: Date.now() + IDENTITY_CACHE_TTL_MS, mtimeMs })
|
|
228
243
|
return identity
|
|
229
244
|
}
|
|
230
245
|
} catch { /* ignore */ }
|
|
231
246
|
const empty: Record<string, string> = {}
|
|
232
|
-
identityFileCache.set(cwd, { data: empty, expiresAt: Date.now() + IDENTITY_CACHE_TTL_MS })
|
|
247
|
+
identityFileCache.set(cwd, { data: empty, expiresAt: Date.now() + IDENTITY_CACHE_TTL_MS, mtimeMs: null })
|
|
233
248
|
return empty
|
|
234
249
|
}
|
|
235
250
|
|
|
@@ -300,11 +315,11 @@ export function isHeartbeatContentEffectivelyEmpty(content: string | undefined |
|
|
|
300
315
|
}
|
|
301
316
|
|
|
302
317
|
export function buildAgentHeartbeatPrompt(
|
|
303
|
-
session:
|
|
304
|
-
agent:
|
|
318
|
+
session: HeartbeatPromptSession,
|
|
319
|
+
agent: HeartbeatPromptAgent | null | undefined,
|
|
305
320
|
fallbackPrompt: string,
|
|
306
321
|
heartbeatFileContent: string,
|
|
307
|
-
opts?: { approvals?: Record<string,
|
|
322
|
+
opts?: { approvals?: Record<string, ApprovalRequest>; chatrooms?: Record<string, Chatroom> },
|
|
308
323
|
): string {
|
|
309
324
|
if (!agent) return fallbackPrompt
|
|
310
325
|
|
|
@@ -342,12 +357,12 @@ export function buildAgentHeartbeatPrompt(
|
|
|
342
357
|
}
|
|
343
358
|
|
|
344
359
|
// ── Phase 3: Goal ancestry ──
|
|
345
|
-
const missionId = session.missionId || agent.missionId || null
|
|
360
|
+
const missionId = (session.missionId || (agent as Record<string, unknown>).missionId || null) as string | null
|
|
346
361
|
const goalAncestry = buildGoalAncestrySection(missionId)
|
|
347
362
|
if (goalAncestry) sections.push(goalAncestry)
|
|
348
363
|
|
|
349
364
|
// ── Phase 4: Active task checkout & events ──
|
|
350
|
-
const events = drainSystemEvents(session.id)
|
|
365
|
+
const events = drainSystemEvents(session.id!)
|
|
351
366
|
if (events.length > 0) {
|
|
352
367
|
const eventBlock = events.map((e) => `- [${new Date(e.timestamp).toISOString()}] ${e.text}`).join('\n')
|
|
353
368
|
sections.push(`Events since last heartbeat:\n${eventBlock}`)
|
|
@@ -369,9 +384,9 @@ export function buildAgentHeartbeatPrompt(
|
|
|
369
384
|
const effectiveFileContent = isHeartbeatContentEffectivelyEmpty(strippedContent) ? '' : strippedContent
|
|
370
385
|
if (effectiveFileContent) sections.push(`\nHEARTBEAT.md contents:\n${effectiveFileContent.slice(0, 2000)}`)
|
|
371
386
|
|
|
372
|
-
const recentMessages = (session.messages
|
|
387
|
+
const recentMessages = (Array.isArray(session.messages) ? session.messages : []).slice(-5) as HeartbeatPromptMessage[]
|
|
373
388
|
const recentContext = recentMessages
|
|
374
|
-
.map((m
|
|
389
|
+
.map((m) => {
|
|
375
390
|
const text = (m.text || '').slice(0, 200)
|
|
376
391
|
const tools = Array.isArray(m.toolEvents) && m.toolEvents.length > 0
|
|
377
392
|
? ` [tools used: ${m.toolEvents.map((t: { name: string }) => t.name).join(', ')}]`
|
|
@@ -386,7 +401,7 @@ export function buildAgentHeartbeatPrompt(
|
|
|
386
401
|
const chatrooms = Object.values(opts?.chatrooms ?? loadChatrooms()) as Chatroom[]
|
|
387
402
|
const myChatrooms = chatrooms.filter((c) => !c.archivedAt && c.agentIds?.includes(agentId))
|
|
388
403
|
if (myChatrooms.length > 0) {
|
|
389
|
-
const lastHeartbeat = state.lastBySession.get(session.id) || 0
|
|
404
|
+
const lastHeartbeat = state.lastBySession.get(session.id!) || 0
|
|
390
405
|
const chatroomLines = myChatrooms
|
|
391
406
|
.map((c) => {
|
|
392
407
|
const recent = (c.messages || []).filter((m: { time: number }) => m.time > lastHeartbeat)
|
|
@@ -422,36 +437,42 @@ export function buildAgentHeartbeatPrompt(
|
|
|
422
437
|
return sections.filter(Boolean).join('\n')
|
|
423
438
|
}
|
|
424
439
|
|
|
425
|
-
function resolveInterval(obj:
|
|
440
|
+
function resolveInterval(obj: object, currentSec: number): number {
|
|
441
|
+
const r = obj as Record<string, unknown>
|
|
426
442
|
// Prefer heartbeatInterval (duration string) over heartbeatIntervalSec (raw number)
|
|
427
|
-
if (
|
|
428
|
-
return parseDuration(
|
|
443
|
+
if (r.heartbeatInterval !== undefined && r.heartbeatInterval !== null) {
|
|
444
|
+
return parseDuration(r.heartbeatInterval, currentSec)
|
|
429
445
|
}
|
|
430
|
-
if (
|
|
431
|
-
return parseIntBounded(
|
|
446
|
+
if (r.heartbeatIntervalSec !== undefined && r.heartbeatIntervalSec !== null) {
|
|
447
|
+
return parseIntBounded(r.heartbeatIntervalSec, currentSec, 0, 86400)
|
|
432
448
|
}
|
|
433
449
|
return currentSec
|
|
434
450
|
}
|
|
435
451
|
|
|
436
|
-
function resolveStr(obj:
|
|
437
|
-
const val = obj[key]
|
|
452
|
+
function resolveStr(obj: object, key: string, current: string | null): string | null {
|
|
453
|
+
const val = (obj as Record<string, unknown>)[key]
|
|
438
454
|
if (typeof val === 'string' && val.trim()) return val.trim()
|
|
439
455
|
return current
|
|
440
456
|
}
|
|
441
457
|
|
|
442
|
-
function resolveBool(obj:
|
|
443
|
-
|
|
444
|
-
if (
|
|
458
|
+
function resolveBool(obj: object, key: string, current: boolean): boolean {
|
|
459
|
+
const r = obj as Record<string, unknown>
|
|
460
|
+
if (r[key] === true) return true
|
|
461
|
+
if (r[key] === false) return false
|
|
445
462
|
return current
|
|
446
463
|
}
|
|
447
464
|
|
|
448
|
-
function resolveNum(obj:
|
|
449
|
-
const val = obj[key]
|
|
465
|
+
function resolveNum(obj: object, key: string, current: number): number {
|
|
466
|
+
const val = (obj as Record<string, unknown>)[key]
|
|
450
467
|
if (typeof val === 'number' && Number.isFinite(val)) return Math.trunc(val)
|
|
451
468
|
return current
|
|
452
469
|
}
|
|
453
470
|
|
|
454
|
-
export function heartbeatConfigForSession(
|
|
471
|
+
export function heartbeatConfigForSession(
|
|
472
|
+
session: HeartbeatPromptSession,
|
|
473
|
+
settings: Partial<AppSettings>,
|
|
474
|
+
agents: Record<string, HeartbeatPromptAgent>,
|
|
475
|
+
): HeartbeatConfig {
|
|
455
476
|
// Global defaults — 30 min interval (was 120s)
|
|
456
477
|
let intervalSec = resolveInterval(settings, DEFAULT_HEARTBEAT_INTERVAL_SEC)
|
|
457
478
|
const globalPrompt = (typeof settings.heartbeatPrompt === 'string' && settings.heartbeatPrompt.trim())
|
|
@@ -498,7 +519,7 @@ export function heartbeatConfigForSession(session: any, settings: Record<string,
|
|
|
498
519
|
return { enabled: enabled && intervalSec > 0, intervalSec, prompt, model, ackMaxChars, showOk, showAlerts, target, lightContext }
|
|
499
520
|
}
|
|
500
521
|
|
|
501
|
-
function lastUserMessageAt(session:
|
|
522
|
+
function lastUserMessageAt(session: HeartbeatPromptSession): number {
|
|
502
523
|
if (!Array.isArray(session?.messages)) return 0
|
|
503
524
|
for (let i = session.messages.length - 1; i >= 0; i--) {
|
|
504
525
|
const msg = session.messages[i]
|
|
@@ -509,15 +530,15 @@ function lastUserMessageAt(session: any): number {
|
|
|
509
530
|
return 0
|
|
510
531
|
}
|
|
511
532
|
|
|
512
|
-
function resolveHeartbeatUserIdleSec(settings:
|
|
513
|
-
const configured = settings.heartbeatUserIdleSec
|
|
533
|
+
function resolveHeartbeatUserIdleSec(settings: Partial<AppSettings>, fallbackSec: number): number {
|
|
534
|
+
const configured = (settings as Record<string, unknown>).heartbeatUserIdleSec
|
|
514
535
|
if (configured === undefined || configured === null || configured === '') {
|
|
515
536
|
return fallbackSec
|
|
516
537
|
}
|
|
517
538
|
return parseIntBounded(configured, fallbackSec, 0, 86_400)
|
|
518
539
|
}
|
|
519
540
|
|
|
520
|
-
function shouldRunHeartbeats(settings:
|
|
541
|
+
function shouldRunHeartbeats(settings: Partial<AppSettings>): boolean {
|
|
521
542
|
const loopMode = settings.loopMode === 'ongoing' ? 'ongoing' : 'bounded'
|
|
522
543
|
return loopMode === 'ongoing'
|
|
523
544
|
}
|
|
@@ -12,7 +12,9 @@ import {
|
|
|
12
12
|
readHeartbeatFile,
|
|
13
13
|
} from '@/lib/server/runtime/heartbeat-service'
|
|
14
14
|
import { buildMainLoopHeartbeatPrompt, isMainSession } from '@/lib/server/agents/main-agent-loop'
|
|
15
|
-
import {
|
|
15
|
+
import { listAgents } from '@/lib/server/agents/agent-repository'
|
|
16
|
+
import { getSession, listSessions } from '@/lib/server/sessions/session-repository'
|
|
17
|
+
import { loadSettings } from '@/lib/server/settings/settings-repository'
|
|
16
18
|
import {
|
|
17
19
|
enqueueSessionRun,
|
|
18
20
|
getSessionExecutionState,
|
|
@@ -314,9 +316,9 @@ function flushWakes(): void {
|
|
|
314
316
|
|
|
315
317
|
if (!wakes.length) return
|
|
316
318
|
|
|
317
|
-
const agents =
|
|
319
|
+
const agents = listAgents()
|
|
318
320
|
const settings = loadSettings()
|
|
319
|
-
const sessions =
|
|
321
|
+
const sessions = listSessions() as unknown as Record<string, Record<string, unknown>>
|
|
320
322
|
let delayedForRetry = false
|
|
321
323
|
|
|
322
324
|
for (const wake of wakes) {
|
|
@@ -324,7 +326,7 @@ function flushWakes(): void {
|
|
|
324
326
|
const sessionId = resolveWakeSessionId(wake, sessions)
|
|
325
327
|
if (!sessionId) continue
|
|
326
328
|
|
|
327
|
-
const session = (sessions[sessionId] ||
|
|
329
|
+
const session = (sessions[sessionId] || getSession(sessionId)) as unknown as Record<string, unknown> | undefined
|
|
328
330
|
if (!session) continue
|
|
329
331
|
|
|
330
332
|
let execution = getSessionExecutionState(sessionId)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { listSessions } from '@/lib/server/sessions/session-repository'
|
|
2
2
|
import type { Session } from '@/types'
|
|
3
3
|
import { log } from '@/lib/server/logger'
|
|
4
4
|
|
|
@@ -26,7 +26,7 @@ const state: IdleWindowState = {
|
|
|
26
26
|
export async function isIdleWindow(options?: { thresholdMs?: number }): Promise<boolean> {
|
|
27
27
|
const threshold = options?.thresholdMs ?? DEFAULT_IDLE_THRESHOLD_MS
|
|
28
28
|
const now = Date.now()
|
|
29
|
-
const sessions =
|
|
29
|
+
const sessions = listSessions()
|
|
30
30
|
|
|
31
31
|
for (const session of Object.values(sessions) as unknown as Session[]) {
|
|
32
32
|
if (!session?.id) continue
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import os from 'os'
|
|
2
|
+
|
|
3
|
+
export function localIP(): string {
|
|
4
|
+
for (const interfaces of Object.values(os.networkInterfaces())) {
|
|
5
|
+
if (!interfaces) continue
|
|
6
|
+
for (const network of interfaces) {
|
|
7
|
+
if (network.family === 'IPv4' && !network.internal) return network.address
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return 'localhost'
|
|
11
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { enqueueOrchestratorEvent } from '@/lib/server/runtime/system-events'
|
|
2
2
|
import { isOrchestratorEligible } from '@/lib/orchestrator-config'
|
|
3
|
-
import {
|
|
3
|
+
import { listAgents } from '@/lib/server/agents/agent-repository'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Broadcast an event to all orchestrator-enabled agents.
|
|
@@ -8,7 +8,7 @@ import { loadAgents } from '@/lib/server/storage'
|
|
|
8
8
|
*/
|
|
9
9
|
export function notifyOrchestrators(text: string, contextKey?: string): void {
|
|
10
10
|
try {
|
|
11
|
-
const agents =
|
|
11
|
+
const agents = listAgents()
|
|
12
12
|
for (const agent of Object.values(agents)) {
|
|
13
13
|
if (agent.orchestratorEnabled && !agent.disabled && !agent.trashedAt && isOrchestratorEligible(agent)) {
|
|
14
14
|
enqueueOrchestratorEvent(agent.id, text, contextKey)
|