@swarmclawai/swarmclaw 1.2.6 → 1.2.9
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 +54 -23
- package/next.config.ts +1 -0
- package/package.json +4 -3
- package/scripts/easy-setup.mjs +1 -1
- package/scripts/postinstall.mjs +1 -1
- package/skills/swarmclaw.md +115 -0
- package/skills/tools/browser.md +131 -0
- package/skills/tools/execute.md +98 -0
- package/skills/tools/files.md +98 -0
- package/skills/tools/memory.md +104 -0
- package/skills/tools/platform.md +144 -0
- package/skills/tools/skills.md +83 -0
- package/src/app/agents/[id]/page.tsx +1 -18
- package/src/app/api/agents/thread-route.test.ts +0 -1
- package/src/app/api/approvals/route.test.ts +6 -22
- package/src/app/api/chats/[id]/messages/route.ts +23 -19
- package/src/app/api/chats/messages-route.test.ts +105 -51
- package/src/app/api/connectors/route.ts +2 -2
- package/src/app/api/mcp-servers/[id]/test/route.ts +3 -2
- package/src/app/api/openclaw/deploy/route.ts +2 -0
- package/src/app/api/portability/export/route.ts +8 -0
- package/src/app/api/portability/import/route.test.ts +80 -0
- package/src/app/api/portability/import/route.ts +28 -0
- package/src/app/api/settings/route.ts +0 -2
- package/src/app/api/setup/doctor/route.ts +4 -4
- package/src/app/api/wallets/[id]/route.ts +15 -157
- package/src/app/api/wallets/generate/route.ts +22 -0
- package/src/app/api/wallets/route.test.ts +147 -0
- package/src/app/api/wallets/route.ts +13 -95
- package/src/app/autonomy/page.tsx +2 -57
- package/src/app/protocols/page.tsx +2 -21
- package/src/app/settings/page.tsx +0 -9
- package/src/app/wallets/page.tsx +105 -5
- package/src/cli/index.js +21 -33
- package/src/cli/spec.js +19 -30
- package/src/components/agents/agent-chat-list.tsx +23 -1
- package/src/components/agents/agent-sheet.tsx +2 -40
- package/src/components/agents/inspector-panel.tsx +165 -131
- package/src/components/chat/chat-area.tsx +38 -9
- package/src/components/chat/chat-card.tsx +0 -31
- package/src/components/chat/message-bubble.tsx +1 -108
- package/src/components/chat/message-list.tsx +33 -19
- package/src/components/connectors/connector-sheet.tsx +25 -1
- package/src/components/gateways/gateway-sheet.tsx +5 -2
- package/src/components/layout/sidebar-rail.tsx +6 -10
- package/src/components/projects/project-detail.tsx +3 -35
- package/src/components/projects/tabs/overview-tab.tsx +3 -59
- package/src/components/projects/tabs/work-tab.tsx +7 -77
- package/src/components/protocols/structured-session-launcher.tsx +1 -22
- package/src/components/shared/connector-platform-icon.tsx +1 -0
- package/src/components/tasks/task-card.tsx +4 -34
- package/src/components/tasks/task-sheet.tsx +6 -36
- package/src/components/wallets/wallet-list.tsx +150 -0
- package/src/lib/agent-execute-defaults.test.ts +24 -0
- package/src/lib/agent-execute-defaults.ts +62 -0
- package/src/lib/app/navigation.test.ts +0 -13
- package/src/lib/app/navigation.ts +2 -7
- package/src/lib/app/view-constants.ts +14 -19
- package/src/lib/chat/queued-message-queue.test.ts +134 -1
- package/src/lib/chat/queued-message-queue.ts +77 -2
- package/src/lib/server/agents/agent-service.ts +5 -0
- package/src/lib/server/agents/agent-thread-session.ts +0 -1
- package/src/lib/server/agents/delegation-advisory.test.ts +0 -1
- package/src/lib/server/agents/delegation-jobs.test.ts +0 -69
- package/src/lib/server/agents/delegation-jobs.ts +0 -25
- package/src/lib/server/agents/main-agent-loop.ts +1 -49
- package/src/lib/server/agents/subagent-runtime.ts +0 -1
- package/src/lib/server/approval-match.ts +0 -85
- package/src/lib/server/approvals.test.ts +6 -6
- package/src/lib/server/approvals.ts +0 -6
- package/src/lib/server/autonomy/supervisor-reflection.test.ts +0 -1
- package/src/lib/server/builtin-extensions.ts +1 -2
- package/src/lib/server/capability-router.test.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-advanced.test.ts +1 -1
- package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +15 -14
- package/src/lib/server/chat-execution/chat-execution-types.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-utils.ts +2 -4
- package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -30
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +1 -36
- package/src/lib/server/chat-execution/chat-turn-preparation.ts +81 -64
- package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -0
- package/src/lib/server/chat-execution/continuation-evaluator.ts +8 -0
- package/src/lib/server/chat-execution/iteration-event-handler.ts +0 -24
- package/src/lib/server/chat-execution/memory-mutation-tools.ts +1 -1
- package/src/lib/server/chat-execution/message-classifier.test.ts +0 -45
- package/src/lib/server/chat-execution/message-classifier.ts +11 -16
- package/src/lib/server/chat-execution/prompt-builder.test.ts +27 -0
- package/src/lib/server/chat-execution/prompt-builder.ts +14 -31
- package/src/lib/server/chat-execution/prompt-mode.test.ts +24 -0
- package/src/lib/server/chat-execution/prompt-mode.ts +5 -1
- package/src/lib/server/chat-execution/prompt-sections.ts +0 -1
- package/src/lib/server/chat-execution/situational-awareness.test.ts +2 -73
- package/src/lib/server/chat-execution/situational-awareness.ts +4 -38
- package/src/lib/server/chat-execution/stream-agent-chat.test.ts +13 -126
- package/src/lib/server/chat-execution/stream-agent-chat.ts +46 -21
- package/src/lib/server/chat-execution/stream-continuation.test.ts +4 -52
- package/src/lib/server/chat-execution/stream-continuation.ts +6 -48
- package/src/lib/server/chatrooms/chatroom-routing.test.ts +4 -0
- package/src/lib/server/chatrooms/session-mailbox.ts +0 -10
- package/src/lib/server/chats/chat-session-service.ts +3 -5
- package/src/lib/server/connectors/connector-inbound.ts +0 -1
- package/src/lib/server/connectors/connector-lifecycle.ts +19 -3
- package/src/lib/server/connectors/connector-service.ts +39 -9
- package/src/lib/server/connectors/discord.ts +2 -2
- package/src/lib/server/connectors/matrix.ts +3 -2
- package/src/lib/server/connectors/signal.ts +5 -4
- package/src/lib/server/connectors/slack.ts +10 -9
- package/src/lib/server/connectors/swarmdock-bidding.ts +74 -0
- package/src/lib/server/connectors/swarmdock-payloads.test.ts +85 -0
- package/src/lib/server/connectors/swarmdock-secret.test.ts +128 -0
- package/src/lib/server/connectors/swarmdock-secret.ts +152 -0
- package/src/lib/server/connectors/swarmdock-tasks.ts +119 -0
- package/src/lib/server/connectors/swarmdock.ts +255 -0
- package/src/lib/server/connectors/teams.ts +3 -2
- package/src/lib/server/connectors/telegram.ts +4 -4
- package/src/lib/server/connectors/whatsapp.ts +2 -2
- package/src/lib/server/daemon/controller.ts +7 -0
- package/src/lib/server/execution-brief.test.ts +2 -25
- package/src/lib/server/execution-brief.ts +12 -35
- package/src/lib/server/execution-engine/task-attempt.ts +0 -1
- package/src/lib/server/gateways/gateway-profile-service.ts +19 -1
- package/src/lib/server/messages/message-repository.test.ts +70 -0
- package/src/lib/server/messages/message-repository.ts +11 -6
- package/src/lib/server/openclaw/deploy.ts +32 -2
- package/src/lib/server/persistence/storage-context.ts +0 -5
- package/src/lib/server/plugins-advanced.test.ts +1 -2
- package/src/lib/server/portability/export.ts +109 -0
- package/src/lib/server/portability/import.ts +159 -0
- package/src/lib/server/protocols/protocol-normalization.ts +0 -4
- package/src/lib/server/protocols/protocol-queries.ts +0 -6
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +4 -32
- package/src/lib/server/protocols/protocol-service.ts +0 -1
- package/src/lib/server/protocols/protocol-step-helpers.ts +0 -4
- package/src/lib/server/protocols/protocol-step-processors.ts +0 -6
- package/src/lib/server/protocols/protocol-swarm.ts +0 -2
- package/src/lib/server/protocols/protocol-types.ts +0 -2
- package/src/lib/server/provider-health.ts +1 -10
- package/src/lib/server/runtime/daemon-state/core.ts +0 -9
- package/src/lib/server/runtime/daemon-state.test.ts +0 -35
- package/src/lib/server/runtime/heartbeat-service.ts +3 -23
- package/src/lib/server/runtime/process-manager.ts +13 -9
- package/src/lib/server/runtime/queue/core.ts +11 -33
- package/src/lib/server/runtime/runtime-storage-write-paths.test.ts +6 -6
- package/src/lib/server/runtime/scheduler.ts +0 -13
- package/src/lib/server/runtime/session-run-manager/drain.ts +0 -24
- package/src/lib/server/runtime/session-run-manager/enqueue.ts +0 -1
- package/src/lib/server/runtime/session-run-manager/queries.ts +15 -1
- package/src/lib/server/runtime/session-run-manager/recovery.ts +0 -1
- package/src/lib/server/runtime/session-run-manager.test.ts +58 -28
- package/src/lib/server/sandbox/session-runtime.test.ts +18 -1
- package/src/lib/server/sandbox/session-runtime.ts +40 -28
- package/src/lib/server/session-tools/autonomy-tools.test.ts +7 -9
- package/src/lib/server/session-tools/context.ts +1 -1
- package/src/lib/server/session-tools/credential-env.ts +109 -0
- package/src/lib/server/session-tools/crud.ts +3 -17
- package/src/lib/server/session-tools/delegate.ts +0 -4
- package/src/lib/server/session-tools/edit_file.ts +3 -2
- package/src/lib/server/session-tools/execute.test.ts +58 -0
- package/src/lib/server/session-tools/execute.ts +334 -0
- package/src/lib/server/session-tools/files-tool.ts +635 -0
- package/src/lib/server/session-tools/index.ts +14 -8
- package/src/lib/server/session-tools/memory-tool.ts +242 -0
- package/src/lib/server/session-tools/memory.ts +1 -1
- package/src/lib/server/session-tools/openclaw-nodes.ts +3 -2
- package/src/lib/server/session-tools/openclaw-workspace.ts +3 -2
- package/src/lib/server/session-tools/platform-tool.ts +617 -0
- package/src/lib/server/session-tools/session-info.ts +3 -2
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +3 -4
- package/src/lib/server/session-tools/shell.ts +7 -122
- package/src/lib/server/session-tools/skills-tool.ts +396 -0
- package/src/lib/server/session-tools/team-context.ts +0 -3
- package/src/lib/server/session-tools/web.ts +2 -2
- package/src/lib/server/storage-normalization.ts +10 -0
- package/src/lib/server/storage.ts +18 -45
- package/src/lib/server/tasks/task-checkout.ts +59 -0
- package/src/lib/server/tasks/task-lifecycle.ts +2 -0
- package/src/lib/server/tasks/task-route-service.ts +4 -26
- package/src/lib/server/tasks/task-service.ts +0 -7
- package/src/lib/server/tool-aliases.ts +2 -2
- package/src/lib/server/tool-capability-policy-advanced.test.ts +13 -6
- package/src/lib/server/tool-capability-policy.test.ts +2 -1
- package/src/lib/server/tool-capability-policy.ts +60 -35
- package/src/lib/server/tool-planning.ts +11 -12
- package/src/lib/server/universal-tool-access.ts +0 -1
- package/src/lib/server/wallets/wallet-crypto.ts +33 -0
- package/src/lib/server/wallets/wallet-repository.ts +24 -0
- package/src/lib/server/wallets/wallet-service.ts +119 -0
- package/src/lib/server/working-state/extraction.ts +8 -42
- package/src/lib/server/working-state/normalization.ts +10 -103
- package/src/lib/server/working-state/service.ts +12 -21
- package/src/lib/setup-defaults.ts +5 -0
- package/src/lib/strip-internal-metadata.test.ts +1 -1
- package/src/lib/strip-internal-metadata.ts +1 -1
- package/src/lib/tool-definitions.ts +1 -1
- package/src/lib/validation/schemas.test.ts +16 -0
- package/src/lib/validation/schemas.ts +49 -2
- package/src/stores/slices/data-slice.ts +5 -1
- package/src/stores/slices/ui-slice.ts +0 -4
- package/src/stores/use-chat-store.test.ts +231 -0
- package/src/stores/use-chat-store.ts +62 -13
- package/src/types/agent.ts +264 -0
- package/src/types/app-settings.ts +173 -0
- package/src/types/approval.ts +25 -0
- package/src/types/connector.ts +188 -0
- package/src/types/extension.ts +386 -0
- package/src/types/index.ts +16 -3555
- package/src/types/message.ts +56 -0
- package/src/types/misc.ts +737 -0
- package/src/types/protocol.ts +420 -0
- package/src/types/provider.ts +52 -0
- package/src/types/run.ts +180 -0
- package/src/types/schedule.ts +59 -0
- package/src/types/session.ts +215 -0
- package/src/types/skill.ts +157 -0
- package/src/types/swarmdock.ts +29 -0
- package/src/types/task.ts +144 -0
- package/src/types/working-state.ts +204 -0
- package/src/views/settings/section-heartbeat.tsx +2 -2
- package/src/views/settings/section-runtime-loop.tsx +0 -14
- package/src/app/api/canvas/[sessionId]/route.ts +0 -35
- package/src/app/api/missions/[id]/actions/route.ts +0 -31
- package/src/app/api/missions/[id]/events/route.ts +0 -14
- package/src/app/api/missions/[id]/route.ts +0 -10
- package/src/app/api/missions/route.test.ts +0 -244
- package/src/app/api/missions/route.ts +0 -57
- package/src/app/api/wallets/[id]/approve/route.ts +0 -79
- package/src/app/api/wallets/[id]/balance-history/route.ts +0 -18
- package/src/app/api/wallets/[id]/send/route.ts +0 -113
- package/src/app/api/wallets/[id]/transactions/route.ts +0 -18
- package/src/app/missions/[id]/page.tsx +0 -3
- package/src/app/missions/page.tsx +0 -685
- package/src/components/canvas/canvas-panel.tsx +0 -267
- package/src/components/wallets/wallet-approval-dialog.tsx +0 -107
- package/src/components/wallets/wallet-panel.tsx +0 -1010
- package/src/components/wallets/wallet-section.tsx +0 -260
- package/src/features/missions/queries.ts +0 -23
- package/src/lib/canvas-content.test.ts +0 -360
- package/src/lib/canvas-content.ts +0 -198
- package/src/lib/server/canvas-content.test.ts +0 -32
- package/src/lib/server/canvas-content.ts +0 -6
- package/src/lib/server/ethereum.ts +0 -591
- package/src/lib/server/evm-swap.ts +0 -476
- package/src/lib/server/missions/mission-intent.test.ts +0 -63
- package/src/lib/server/missions/mission-intent.ts +0 -569
- package/src/lib/server/missions/mission-repository.ts +0 -74
- package/src/lib/server/missions/mission-service/actions.ts +0 -6
- package/src/lib/server/missions/mission-service/bindings.ts +0 -9
- package/src/lib/server/missions/mission-service/context.ts +0 -4
- package/src/lib/server/missions/mission-service/core.ts +0 -2271
- package/src/lib/server/missions/mission-service/queries.ts +0 -12
- package/src/lib/server/missions/mission-service/recovery.ts +0 -5
- package/src/lib/server/missions/mission-service/ticks.ts +0 -9
- package/src/lib/server/missions/mission-service.test.ts +0 -888
- package/src/lib/server/missions/mission-service.ts +0 -6
- package/src/lib/server/session-tools/canvas.ts +0 -105
- package/src/lib/server/session-tools/sandbox.ts +0 -281
- package/src/lib/server/session-tools/wallet-tool.test.ts +0 -150
- package/src/lib/server/session-tools/wallet.ts +0 -1287
- package/src/lib/server/solana.ts +0 -327
- package/src/lib/server/wallet/wallet-execution.test.ts +0 -198
- package/src/lib/server/wallet/wallet-portfolio.test.ts +0 -98
- package/src/lib/server/wallet/wallet-portfolio.ts +0 -772
- package/src/lib/server/wallet/wallet-service.test.ts +0 -81
- package/src/lib/server/wallet/wallet-service.ts +0 -225
- package/src/lib/wallet/wallet-transactions.test.ts +0 -75
- package/src/lib/wallet/wallet-transactions.ts +0 -43
- package/src/lib/wallet/wallet.test.ts +0 -333
- package/src/lib/wallet/wallet.ts +0 -183
- package/src/views/settings/section-wallets.tsx +0 -35
|
@@ -41,6 +41,21 @@ const AgentSandboxConfigSchema = z.object({
|
|
|
41
41
|
prune: AgentSandboxPruneSchema,
|
|
42
42
|
}).nullable().optional()
|
|
43
43
|
|
|
44
|
+
const AgentExecuteConfigSchema = z.object({
|
|
45
|
+
backend: z.enum(['sandbox', 'host']).optional(),
|
|
46
|
+
network: z.object({
|
|
47
|
+
enabled: z.boolean(),
|
|
48
|
+
allowedUrls: z.array(z.string()).optional(),
|
|
49
|
+
}).optional(),
|
|
50
|
+
runtimes: z.object({
|
|
51
|
+
python: z.boolean().optional(),
|
|
52
|
+
javascript: z.boolean().optional(),
|
|
53
|
+
sqlite: z.boolean().optional(),
|
|
54
|
+
}).optional(),
|
|
55
|
+
timeout: z.number().int().positive().optional(),
|
|
56
|
+
credentials: z.array(z.string()).optional(),
|
|
57
|
+
}).nullable().optional()
|
|
58
|
+
|
|
44
59
|
const AgentRoutingTargetSchema = z.object({
|
|
45
60
|
id: z.string().min(1),
|
|
46
61
|
label: z.string().optional(),
|
|
@@ -110,6 +125,7 @@ export const AgentCreateSchema = z.object({
|
|
|
110
125
|
avatarSeed: z.string().optional(),
|
|
111
126
|
avatarUrl: z.string().nullable().optional().default(null),
|
|
112
127
|
sandboxConfig: AgentSandboxConfigSchema,
|
|
128
|
+
executeConfig: AgentExecuteConfigSchema,
|
|
113
129
|
autoRecovery: z.boolean().optional().default(false),
|
|
114
130
|
monthlyBudget: z.number().positive().nullable().optional().default(null),
|
|
115
131
|
dailyBudget: z.number().positive().nullable().optional().default(null),
|
|
@@ -121,7 +137,7 @@ export const ConnectorCreateSchema = z.object({
|
|
|
121
137
|
name: z.string().min(1, 'Connector name is required').optional(),
|
|
122
138
|
platform: z.enum([
|
|
123
139
|
'discord', 'telegram', 'slack', 'whatsapp', 'openclaw',
|
|
124
|
-
'bluebubbles', 'signal', 'teams', 'googlechat', 'matrix', 'email',
|
|
140
|
+
'bluebubbles', 'signal', 'teams', 'googlechat', 'matrix', 'email', 'swarmdock',
|
|
125
141
|
]),
|
|
126
142
|
agentId: z.string().nullable().optional().default(null),
|
|
127
143
|
chatroomId: z.string().nullable().optional().default(null),
|
|
@@ -380,7 +396,6 @@ export const ProtocolRunCreateSchema = z.object({
|
|
|
380
396
|
participantAgentIds: z.array(z.string()).min(1, 'Select at least one participant').default([]),
|
|
381
397
|
facilitatorAgentId: z.string().nullable().optional().default(null),
|
|
382
398
|
observerAgentIds: z.array(z.string()).optional().default([]),
|
|
383
|
-
missionId: z.string().nullable().optional().default(null),
|
|
384
399
|
taskId: z.string().nullable().optional().default(null),
|
|
385
400
|
sessionId: z.string().nullable().optional().default(null),
|
|
386
401
|
parentChatroomId: z.string().nullable().optional().default(null),
|
|
@@ -457,6 +472,38 @@ export const ProtocolRunActionSchema = z.object({
|
|
|
457
472
|
workItemId: z.string().nullable().optional().default(null),
|
|
458
473
|
})
|
|
459
474
|
|
|
475
|
+
const PortableAgentSchema = z.object({
|
|
476
|
+
originalId: z.string().min(1),
|
|
477
|
+
name: z.string().min(1),
|
|
478
|
+
description: z.string(),
|
|
479
|
+
systemPrompt: z.string(),
|
|
480
|
+
provider: z.string().min(1),
|
|
481
|
+
model: z.string().min(1),
|
|
482
|
+
createdAt: z.number().optional(),
|
|
483
|
+
updatedAt: z.number().optional(),
|
|
484
|
+
skillIds: z.array(z.string()).optional(),
|
|
485
|
+
}).passthrough()
|
|
486
|
+
|
|
487
|
+
const PortableSkillSchema = z.object({
|
|
488
|
+
originalId: z.string().min(1),
|
|
489
|
+
name: z.string().min(1),
|
|
490
|
+
content: z.string(),
|
|
491
|
+
}).passthrough()
|
|
492
|
+
|
|
493
|
+
const PortableScheduleSchema = z.object({
|
|
494
|
+
originalId: z.string().min(1),
|
|
495
|
+
originalAgentId: z.string().min(1),
|
|
496
|
+
name: z.string().min(1),
|
|
497
|
+
}).passthrough()
|
|
498
|
+
|
|
499
|
+
export const PortableManifestSchema = z.object({
|
|
500
|
+
formatVersion: z.number().int().nonnegative(),
|
|
501
|
+
exportedAt: z.string().optional(),
|
|
502
|
+
agents: z.array(PortableAgentSchema),
|
|
503
|
+
skills: z.array(PortableSkillSchema),
|
|
504
|
+
schedules: z.array(PortableScheduleSchema),
|
|
505
|
+
})
|
|
506
|
+
|
|
460
507
|
/** Format ZodError into a 400-friendly payload */
|
|
461
508
|
export function formatZodError(err: z.ZodError) {
|
|
462
509
|
return { error: 'Validation failed', issues: err.issues.map((i) => ({ path: i.path.join('.'), message: i.message })) }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StateCreator } from 'zustand'
|
|
2
2
|
import type { AppState } from '../use-app-store'
|
|
3
|
-
import type { NetworkInfo, Directory, ProviderInfo, Credentials, Schedule, AppSettings, StoredSecret, ProviderConfig, Skill, Connector, Webhook, McpServerConfig, ExtensionMeta, Project, ActivityEntry, AppNotification, GatewayProfile } from '../../types'
|
|
3
|
+
import type { NetworkInfo, Directory, ProviderInfo, Credentials, Schedule, AppSettings, StoredSecret, ProviderConfig, Skill, Connector, Webhook, McpServerConfig, ExtensionMeta, Project, ActivityEntry, AppNotification, GatewayProfile, SafeWallet } from '../../types'
|
|
4
4
|
import { api } from '@/lib/app/api-client'
|
|
5
5
|
import { safeStorageGetJson, safeStorageSet } from '@/lib/app/safe-storage'
|
|
6
6
|
import { fetchDirs, fetchProviders, fetchCredentials } from '@/lib/chat/chats'
|
|
@@ -24,6 +24,8 @@ export interface DataSlice {
|
|
|
24
24
|
updateSettings: (patch: Partial<AppSettings>) => Promise<void>
|
|
25
25
|
secrets: Record<string, StoredSecret>
|
|
26
26
|
loadSecrets: () => Promise<void>
|
|
27
|
+
wallets: Record<string, SafeWallet>
|
|
28
|
+
loadWallets: () => Promise<void>
|
|
27
29
|
providerConfigs: ProviderConfig[]
|
|
28
30
|
loadProviderConfigs: () => Promise<void>
|
|
29
31
|
gatewayProfiles: GatewayProfile[]
|
|
@@ -78,6 +80,8 @@ export const createDataSlice: StateCreator<AppState, [], [], DataSlice> = (set,
|
|
|
78
80
|
},
|
|
79
81
|
secrets: {},
|
|
80
82
|
loadSecrets: createLoader<AppState>(set, 'secrets', () => api<Record<string, StoredSecret>>('GET', '/secrets'), {}),
|
|
83
|
+
wallets: {},
|
|
84
|
+
loadWallets: createLoader<AppState>(set, 'wallets', () => api<Record<string, SafeWallet>>('GET', '/wallets'), {}),
|
|
81
85
|
providerConfigs: [],
|
|
82
86
|
loadProviderConfigs: createLoader<AppState>(set, 'providerConfigs', () => api<ProviderConfig[]>('GET', '/providers/configs'), []),
|
|
83
87
|
gatewayProfiles: [],
|
|
@@ -95,8 +95,6 @@ export interface UiSlice {
|
|
|
95
95
|
setFleetFilter: (filter: FleetFilter) => void
|
|
96
96
|
chatFilter: 'all' | 'active' | 'recent'
|
|
97
97
|
setChatFilter: (filter: 'all' | 'active' | 'recent') => void
|
|
98
|
-
walletPanelAgentId: string | null
|
|
99
|
-
setWalletPanelAgentId: (id: string | null) => void
|
|
100
98
|
heartbeatHistoryOpen: boolean
|
|
101
99
|
setHeartbeatHistoryOpen: (open: boolean) => void
|
|
102
100
|
agentPrefill: Partial<Agent> | null
|
|
@@ -195,8 +193,6 @@ export const createUiSlice: StateCreator<AppState, [], [], UiSlice> = (set, get)
|
|
|
195
193
|
setFleetFilter: (filter) => { safeStorageSet('sc_fleet_filter', filter); set({ fleetFilter: filter }) },
|
|
196
194
|
chatFilter: 'all' as const,
|
|
197
195
|
setChatFilter: (filter) => set({ chatFilter: filter }),
|
|
198
|
-
walletPanelAgentId: null,
|
|
199
|
-
setWalletPanelAgentId: (id) => set({ walletPanelAgentId: id }),
|
|
200
196
|
heartbeatHistoryOpen: false,
|
|
201
197
|
setHeartbeatHistoryOpen: (open) => set({ heartbeatHistoryOpen: open }),
|
|
202
198
|
agentPrefill: null,
|
|
@@ -451,6 +451,103 @@ describe('useChatStore control-token hygiene', () => {
|
|
|
451
451
|
assert.equal(useAppStore.getState().sessions['session-1']?.currentRunId, 'run-active')
|
|
452
452
|
})
|
|
453
453
|
|
|
454
|
+
it('hydrates the active turn from the backend queue snapshot as a sending placeholder', async () => {
|
|
455
|
+
const now = Date.now()
|
|
456
|
+
const session = makeSession()
|
|
457
|
+
useAppStore.setState({
|
|
458
|
+
agents: { 'agent-1': makeAgent() },
|
|
459
|
+
sessions: { [session.id]: session },
|
|
460
|
+
currentAgentId: 'agent-1',
|
|
461
|
+
})
|
|
462
|
+
useChatStore.setState({
|
|
463
|
+
messages: [],
|
|
464
|
+
pendingFiles: [],
|
|
465
|
+
replyingTo: null,
|
|
466
|
+
toolEvents: [],
|
|
467
|
+
streamText: '',
|
|
468
|
+
displayText: '',
|
|
469
|
+
streaming: false,
|
|
470
|
+
streamingSessionId: null,
|
|
471
|
+
streamSource: null,
|
|
472
|
+
assistantRenderId: null,
|
|
473
|
+
streamPhase: 'thinking',
|
|
474
|
+
streamToolName: '',
|
|
475
|
+
thinkingText: '',
|
|
476
|
+
thinkingStartTime: 0,
|
|
477
|
+
queuedMessages: [],
|
|
478
|
+
agentStatus: null,
|
|
479
|
+
lastUsage: null,
|
|
480
|
+
hasMoreMessages: false,
|
|
481
|
+
loadingMore: false,
|
|
482
|
+
totalMessages: 0,
|
|
483
|
+
})
|
|
484
|
+
|
|
485
|
+
global.fetch = (async (input: RequestInfo | URL) => {
|
|
486
|
+
const url = String(input)
|
|
487
|
+
if (url === '/api/chats/session-1/queue') {
|
|
488
|
+
return jsonResponse({
|
|
489
|
+
sessionId: 'session-1',
|
|
490
|
+
activeRunId: 'run-active',
|
|
491
|
+
activeTurn: {
|
|
492
|
+
runId: 'run-active',
|
|
493
|
+
sessionId: 'session-1',
|
|
494
|
+
text: 'Already running',
|
|
495
|
+
queuedAt: now,
|
|
496
|
+
position: 0,
|
|
497
|
+
},
|
|
498
|
+
queueLength: 1,
|
|
499
|
+
items: [
|
|
500
|
+
{ runId: 'run-queued-2', sessionId: 'session-1', text: 'Then refine it', queuedAt: now + 1, position: 1 },
|
|
501
|
+
],
|
|
502
|
+
})
|
|
503
|
+
}
|
|
504
|
+
throw new Error(`Unexpected fetch: ${url}`)
|
|
505
|
+
}) as unknown as typeof fetch
|
|
506
|
+
|
|
507
|
+
await useChatStore.getState().loadQueuedMessages('session-1')
|
|
508
|
+
|
|
509
|
+
const state = useChatStore.getState()
|
|
510
|
+
assert.deepEqual(
|
|
511
|
+
state.queuedMessages.map((item) => [item.runId, item.sending === true]),
|
|
512
|
+
[['run-active', true], ['run-queued-2', false]],
|
|
513
|
+
)
|
|
514
|
+
assert.equal(useAppStore.getState().sessions['session-1']?.queuedCount, 1)
|
|
515
|
+
assert.equal(useAppStore.getState().sessions['session-1']?.currentRunId, 'run-active')
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
it('clears sending placeholders when a persisted user message with the same runId arrives', () => {
|
|
519
|
+
useChatStore.setState({
|
|
520
|
+
messages: [],
|
|
521
|
+
assistantRenderId: null,
|
|
522
|
+
queuedMessages: [
|
|
523
|
+
{ runId: 'run-active', sessionId: 'session-1', text: 'Already running', queuedAt: 9, position: 0, sending: true },
|
|
524
|
+
],
|
|
525
|
+
toolEvents: [],
|
|
526
|
+
streamText: '',
|
|
527
|
+
displayText: '',
|
|
528
|
+
streaming: false,
|
|
529
|
+
streamingSessionId: null,
|
|
530
|
+
streamSource: null,
|
|
531
|
+
streamPhase: 'thinking',
|
|
532
|
+
streamToolName: '',
|
|
533
|
+
thinkingText: '',
|
|
534
|
+
thinkingStartTime: 0,
|
|
535
|
+
agentStatus: null,
|
|
536
|
+
lastUsage: null,
|
|
537
|
+
hasMoreMessages: false,
|
|
538
|
+
loadingMore: false,
|
|
539
|
+
totalMessages: 0,
|
|
540
|
+
})
|
|
541
|
+
|
|
542
|
+
useChatStore.getState().setMessages([
|
|
543
|
+
{ role: 'user', text: 'Already running', time: 10, runId: 'run-active' },
|
|
544
|
+
])
|
|
545
|
+
|
|
546
|
+
const state = useChatStore.getState()
|
|
547
|
+
assert.equal(state.queuedMessages.length, 0)
|
|
548
|
+
assert.equal(state.messages[0]?.runId, 'run-active')
|
|
549
|
+
})
|
|
550
|
+
|
|
454
551
|
it('removes optimistic queued items again when the backend enqueue fails', async () => {
|
|
455
552
|
const session = makeSession()
|
|
456
553
|
useAppStore.setState({
|
|
@@ -533,4 +630,138 @@ describe('useChatStore control-token hygiene', () => {
|
|
|
533
630
|
assert.equal(state.assistantRenderId, 'render-1')
|
|
534
631
|
assert.equal(state.messages[1]?.clientRenderId, 'render-1')
|
|
535
632
|
})
|
|
633
|
+
|
|
634
|
+
it('preserves transcript totals on local message updates for paginated windows', () => {
|
|
635
|
+
useChatStore.setState({
|
|
636
|
+
messages: [
|
|
637
|
+
{ role: 'user', text: 'Ninth', time: 9, bookmarked: false },
|
|
638
|
+
{ role: 'assistant', text: 'Tenth', time: 10 },
|
|
639
|
+
],
|
|
640
|
+
messageStartIndex: 8,
|
|
641
|
+
assistantRenderId: null,
|
|
642
|
+
toolEvents: [],
|
|
643
|
+
streamText: '',
|
|
644
|
+
displayText: '',
|
|
645
|
+
streaming: false,
|
|
646
|
+
streamingSessionId: null,
|
|
647
|
+
streamSource: null,
|
|
648
|
+
streamPhase: 'thinking',
|
|
649
|
+
streamToolName: '',
|
|
650
|
+
thinkingText: '',
|
|
651
|
+
thinkingStartTime: 0,
|
|
652
|
+
queuedMessages: [],
|
|
653
|
+
agentStatus: null,
|
|
654
|
+
lastUsage: null,
|
|
655
|
+
hasMoreMessages: true,
|
|
656
|
+
loadingMore: false,
|
|
657
|
+
totalMessages: 10,
|
|
658
|
+
})
|
|
659
|
+
|
|
660
|
+
useChatStore.getState().setMessages([
|
|
661
|
+
{ role: 'user', text: 'Ninth', time: 9, bookmarked: true },
|
|
662
|
+
{ role: 'assistant', text: 'Tenth', time: 10 },
|
|
663
|
+
])
|
|
664
|
+
|
|
665
|
+
const state = useChatStore.getState()
|
|
666
|
+
assert.equal(state.messageStartIndex, 8)
|
|
667
|
+
assert.equal(state.totalMessages, 10)
|
|
668
|
+
assert.equal(state.hasMoreMessages, true)
|
|
669
|
+
assert.equal(state.messages[0]?.bookmarked, true)
|
|
670
|
+
})
|
|
671
|
+
|
|
672
|
+
it('does not timeout sending items before 60 seconds', () => {
|
|
673
|
+
const thirtySecondsAgo = Date.now() - 30_000
|
|
674
|
+
useChatStore.setState({
|
|
675
|
+
messages: [],
|
|
676
|
+
assistantRenderId: null,
|
|
677
|
+
toolEvents: [],
|
|
678
|
+
streamText: '',
|
|
679
|
+
displayText: '',
|
|
680
|
+
streaming: false,
|
|
681
|
+
streamingSessionId: null,
|
|
682
|
+
streamSource: null,
|
|
683
|
+
streamPhase: 'thinking',
|
|
684
|
+
streamToolName: '',
|
|
685
|
+
thinkingText: '',
|
|
686
|
+
thinkingStartTime: 0,
|
|
687
|
+
queuedMessages: [
|
|
688
|
+
{ runId: 'run-old', sessionId: 'session-1', text: 'Waiting', queuedAt: thirtySecondsAgo, position: 0, sending: true },
|
|
689
|
+
],
|
|
690
|
+
agentStatus: null,
|
|
691
|
+
lastUsage: null,
|
|
692
|
+
hasMoreMessages: false,
|
|
693
|
+
loadingMore: false,
|
|
694
|
+
totalMessages: 0,
|
|
695
|
+
})
|
|
696
|
+
|
|
697
|
+
// setMessages with no matching persisted message — item should survive
|
|
698
|
+
useChatStore.getState().setMessages([
|
|
699
|
+
{ role: 'user', text: 'Unrelated', time: Date.now() },
|
|
700
|
+
])
|
|
701
|
+
|
|
702
|
+
const state = useChatStore.getState()
|
|
703
|
+
assert.equal(state.queuedMessages.length, 1)
|
|
704
|
+
assert.equal(state.queuedMessages[0]?.runId, 'run-old')
|
|
705
|
+
})
|
|
706
|
+
|
|
707
|
+
it('tracks messageStartIndex when loading more paginated history', async () => {
|
|
708
|
+
const session = makeSession()
|
|
709
|
+
useAppStore.setState({
|
|
710
|
+
agents: { 'agent-1': makeAgent() },
|
|
711
|
+
sessions: { [session.id]: session },
|
|
712
|
+
currentAgentId: 'agent-1',
|
|
713
|
+
})
|
|
714
|
+
useChatStore.setState({
|
|
715
|
+
messages: [
|
|
716
|
+
{ role: 'user', text: 'Ninth', time: 9 },
|
|
717
|
+
{ role: 'assistant', text: 'Tenth', time: 10 },
|
|
718
|
+
],
|
|
719
|
+
messageStartIndex: 8,
|
|
720
|
+
pendingFiles: [],
|
|
721
|
+
replyingTo: null,
|
|
722
|
+
toolEvents: [],
|
|
723
|
+
streamText: '',
|
|
724
|
+
displayText: '',
|
|
725
|
+
streaming: false,
|
|
726
|
+
streamingSessionId: null,
|
|
727
|
+
streamSource: null,
|
|
728
|
+
assistantRenderId: null,
|
|
729
|
+
streamPhase: 'thinking',
|
|
730
|
+
streamToolName: '',
|
|
731
|
+
thinkingText: '',
|
|
732
|
+
thinkingStartTime: 0,
|
|
733
|
+
queuedMessages: [],
|
|
734
|
+
agentStatus: null,
|
|
735
|
+
lastUsage: null,
|
|
736
|
+
hasMoreMessages: true,
|
|
737
|
+
loadingMore: false,
|
|
738
|
+
totalMessages: 10,
|
|
739
|
+
})
|
|
740
|
+
|
|
741
|
+
global.fetch = (async (input: RequestInfo | URL) => {
|
|
742
|
+
const url = String(input)
|
|
743
|
+
if (url === '/api/chats/session-1/messages?limit=100&before=8') {
|
|
744
|
+
return jsonResponse({
|
|
745
|
+
messages: [
|
|
746
|
+
{ role: 'user', text: 'Seventh', time: 7 },
|
|
747
|
+
{ role: 'assistant', text: 'Eighth', time: 8 },
|
|
748
|
+
],
|
|
749
|
+
total: 10,
|
|
750
|
+
hasMore: true,
|
|
751
|
+
startIndex: 6,
|
|
752
|
+
})
|
|
753
|
+
}
|
|
754
|
+
throw new Error(`Unexpected fetch: ${url}`)
|
|
755
|
+
}) as unknown as typeof fetch
|
|
756
|
+
|
|
757
|
+
await useChatStore.getState().loadMoreMessages()
|
|
758
|
+
|
|
759
|
+
const state = useChatStore.getState()
|
|
760
|
+
assert.equal(state.messageStartIndex, 6)
|
|
761
|
+
assert.equal(state.totalMessages, 10)
|
|
762
|
+
assert.deepEqual(
|
|
763
|
+
state.messages.map((message) => message.text),
|
|
764
|
+
['Seventh', 'Eighth', 'Ninth', 'Tenth'],
|
|
765
|
+
)
|
|
766
|
+
})
|
|
536
767
|
})
|
|
@@ -66,7 +66,8 @@ interface ChatState {
|
|
|
66
66
|
agentStatus: { goal?: string; status?: string; summary?: string; nextAction?: string } | null
|
|
67
67
|
|
|
68
68
|
messages: Message[]
|
|
69
|
-
|
|
69
|
+
messageStartIndex: number
|
|
70
|
+
setMessages: (msgs: Message[], options?: { startIndex?: number; totalMessages?: number }) => void
|
|
70
71
|
|
|
71
72
|
toolEvents: ToolEvent[]
|
|
72
73
|
clearToolEvents: () => void
|
|
@@ -136,6 +137,10 @@ interface ChatState {
|
|
|
136
137
|
loadMoreMessages: () => Promise<void>
|
|
137
138
|
}
|
|
138
139
|
|
|
140
|
+
/** Safety-net timeout for "sending" queue items. Normally cleaned up by
|
|
141
|
+
* matchesPersistedQueuedMessage well before this — only fires if matching fails. */
|
|
142
|
+
const SENDING_ITEM_TIMEOUT_MS = 60_000
|
|
143
|
+
|
|
139
144
|
const CONTROL_TOKEN_PREFIX_RE = /^\s*(?:NO_MESSAGE|HEARTBEAT_OK)(?:(?=[\s.,:;!?()[\]{}"'`-]|$)|(?=[A-Z]))\s*/i
|
|
140
145
|
const CONTROL_TOKEN_LINE_RE = /(^|\n)\s*(?:NO_MESSAGE|HEARTBEAT_OK)\s*(\n|$)/gi
|
|
141
146
|
|
|
@@ -165,6 +170,29 @@ function reconcileMessagesForState(
|
|
|
165
170
|
return { messages, assistantRenderId: nextAssistantRenderId }
|
|
166
171
|
}
|
|
167
172
|
|
|
173
|
+
function attachedFilesEqual(left: string[] | undefined, right: string[] | undefined): boolean {
|
|
174
|
+
if (!left?.length && !right?.length) return true
|
|
175
|
+
if ((left?.length || 0) !== (right?.length || 0)) return false
|
|
176
|
+
for (let index = 0; index < (left?.length || 0); index += 1) {
|
|
177
|
+
if (left?.[index] !== right?.[index]) return false
|
|
178
|
+
}
|
|
179
|
+
return true
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function matchesPersistedQueuedMessage(message: Message, queued: QueuedSessionMessage): boolean {
|
|
183
|
+
if (message.role !== 'user') return false
|
|
184
|
+
const messageRunId = typeof message.runId === 'string' && message.runId.trim() ? message.runId : null
|
|
185
|
+
const queuedRunId = typeof queued.runId === 'string' && queued.runId.trim() ? queued.runId : null
|
|
186
|
+
if (messageRunId && queuedRunId) return messageRunId === queuedRunId
|
|
187
|
+
return (
|
|
188
|
+
message.text === queued.text
|
|
189
|
+
&& message.replyToId === queued.replyToId
|
|
190
|
+
&& message.imagePath === queued.imagePath
|
|
191
|
+
&& message.imageUrl === queued.imageUrl
|
|
192
|
+
&& attachedFilesEqual(message.attachedFiles, queued.attachedFiles)
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
168
196
|
function syncSessionQueueState(sessionId: string, params: {
|
|
169
197
|
queuedCount: number
|
|
170
198
|
currentRunId?: string | null
|
|
@@ -203,13 +231,14 @@ export const useChatStore = create<ChatState>((set, get) => ({
|
|
|
203
231
|
displayText: '',
|
|
204
232
|
agentStatus: null,
|
|
205
233
|
messages: [],
|
|
206
|
-
|
|
234
|
+
messageStartIndex: 0,
|
|
235
|
+
setMessages: (msgs, options) => set((s) => {
|
|
207
236
|
const next = reconcileMessagesForState(msgs, s.messages, s.assistantRenderId)
|
|
208
237
|
// Clear "sending" queue items whose text now appears in the message list
|
|
209
238
|
const queuedMessages = s.queuedMessages.filter((item) => {
|
|
210
239
|
if (!item.sending) return true
|
|
211
|
-
if (next.messages.some((
|
|
212
|
-
if (Date.now() - item.queuedAt >
|
|
240
|
+
if (next.messages.some((message) => matchesPersistedQueuedMessage(message, item))) return false
|
|
241
|
+
if (Date.now() - item.queuedAt > SENDING_ITEM_TIMEOUT_MS) return false
|
|
213
242
|
return true
|
|
214
243
|
})
|
|
215
244
|
const patch: Partial<ChatState> = {
|
|
@@ -217,9 +246,29 @@ export const useChatStore = create<ChatState>((set, get) => ({
|
|
|
217
246
|
assistantRenderId: next.assistantRenderId,
|
|
218
247
|
queuedMessages,
|
|
219
248
|
}
|
|
249
|
+
if (typeof options?.startIndex === 'number' && Number.isFinite(options.startIndex)) {
|
|
250
|
+
patch.messageStartIndex = Math.max(0, Math.trunc(options.startIndex))
|
|
251
|
+
} else if (next.messages.length === 0) {
|
|
252
|
+
patch.messageStartIndex = 0
|
|
253
|
+
}
|
|
220
254
|
if (s.toolEvents.length > 0) patch.toolEvents = []
|
|
221
|
-
if (
|
|
222
|
-
|
|
255
|
+
if (next.messages.length === 0) {
|
|
256
|
+
patch.hasMoreMessages = false
|
|
257
|
+
} else if (
|
|
258
|
+
typeof options?.startIndex === 'number'
|
|
259
|
+
&& Number.isFinite(options.startIndex)
|
|
260
|
+
&& Math.trunc(options.startIndex) === 0
|
|
261
|
+
&& typeof options?.totalMessages === 'number'
|
|
262
|
+
&& Number.isFinite(options.totalMessages)
|
|
263
|
+
&& Math.max(0, Math.trunc(options.totalMessages)) === next.messages.length
|
|
264
|
+
) {
|
|
265
|
+
patch.hasMoreMessages = false
|
|
266
|
+
}
|
|
267
|
+
if (typeof options?.totalMessages === 'number' && Number.isFinite(options.totalMessages)) {
|
|
268
|
+
patch.totalMessages = Math.max(0, Math.trunc(options.totalMessages))
|
|
269
|
+
} else if (next.messages.length === 0 && s.totalMessages !== 0) {
|
|
270
|
+
patch.totalMessages = 0
|
|
271
|
+
}
|
|
223
272
|
return patch
|
|
224
273
|
}),
|
|
225
274
|
toolEvents: [],
|
|
@@ -253,8 +302,8 @@ export const useChatStore = create<ChatState>((set, get) => ({
|
|
|
253
302
|
const messages = s.messages
|
|
254
303
|
const cleaned = next.filter((item) => {
|
|
255
304
|
if (!item.sending || item.sessionId !== sessionId) return true
|
|
256
|
-
if (messages.some((
|
|
257
|
-
if (Date.now() - item.queuedAt >
|
|
305
|
+
if (messages.some((message) => matchesPersistedQueuedMessage(message, item))) return false
|
|
306
|
+
if (Date.now() - item.queuedAt > SENDING_ITEM_TIMEOUT_MS) return false
|
|
258
307
|
return true
|
|
259
308
|
})
|
|
260
309
|
return { queuedMessages: cleaned }
|
|
@@ -675,7 +724,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
|
|
|
675
724
|
})
|
|
676
725
|
if (msgsRes.ok) {
|
|
677
726
|
const msgs = await msgsRes.json()
|
|
678
|
-
get().setMessages(msgs)
|
|
727
|
+
get().setMessages(msgs, { startIndex: 0, totalMessages: msgs.length })
|
|
679
728
|
}
|
|
680
729
|
// Re-send with the new text
|
|
681
730
|
await get().sendMessage(newText)
|
|
@@ -703,7 +752,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
|
|
|
703
752
|
})
|
|
704
753
|
if (msgsRes.ok) {
|
|
705
754
|
const msgs = await msgsRes.json()
|
|
706
|
-
get().setMessages(msgs)
|
|
755
|
+
get().setMessages(msgs, { startIndex: 0, totalMessages: msgs.length })
|
|
707
756
|
}
|
|
708
757
|
// Re-send the last user message through the normal SSE flow
|
|
709
758
|
if (imagePath) {
|
|
@@ -817,15 +866,14 @@ export const useChatStore = create<ChatState>((set, get) => ({
|
|
|
817
866
|
loadingMore: false,
|
|
818
867
|
totalMessages: 0,
|
|
819
868
|
loadMoreMessages: async () => {
|
|
820
|
-
const {
|
|
869
|
+
const { loadingMore, hasMoreMessages, messageStartIndex } = get()
|
|
821
870
|
if (loadingMore || !hasMoreMessages) return
|
|
822
871
|
const sessionId = selectActiveSessionId(useAppStore.getState())
|
|
823
872
|
if (!sessionId) return
|
|
824
873
|
set({ loadingMore: true })
|
|
825
874
|
try {
|
|
826
875
|
const key = getStoredAccessKey()
|
|
827
|
-
|
|
828
|
-
const currentStartIndex = totalMessages - messages.length
|
|
876
|
+
const currentStartIndex = messageStartIndex
|
|
829
877
|
const res = await fetch(`/api/chats/${sessionId}/messages?limit=100&before=${currentStartIndex}`, {
|
|
830
878
|
headers: key ? { 'X-Access-Key': key } : undefined,
|
|
831
879
|
})
|
|
@@ -840,6 +888,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
|
|
|
840
888
|
return {
|
|
841
889
|
messages: next.messages,
|
|
842
890
|
assistantRenderId: next.assistantRenderId,
|
|
891
|
+
messageStartIndex: data.startIndex,
|
|
843
892
|
hasMoreMessages: data.hasMore,
|
|
844
893
|
totalMessages: data.total,
|
|
845
894
|
loadingMore: false,
|