@promptbook/cli 0.112.0-132 → 0.112.0-134

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.
Files changed (101) hide show
  1. package/apps/agents-server/src/app/admin/api-tokens/ApiTokensClient.tsx +4 -1
  2. package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +3 -0
  3. package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackTable.tsx +13 -8
  4. package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +3 -0
  5. package/apps/agents-server/src/app/admin/chat-history/ChatHistoryTable.tsx +13 -8
  6. package/apps/agents-server/src/app/admin/custom-css/CustomCssClient.tsx +4 -0
  7. package/apps/agents-server/src/app/admin/custom-css/CustomCssEditorPanel.tsx +7 -1
  8. package/apps/agents-server/src/app/admin/custom-css/CustomCssFilesPanel.tsx +7 -1
  9. package/apps/agents-server/src/app/admin/custom-js/CustomJsClient.tsx +4 -0
  10. package/apps/agents-server/src/app/admin/custom-js/CustomJsEditorPanel.tsx +7 -1
  11. package/apps/agents-server/src/app/admin/custom-js/CustomJsFilesPanel.tsx +7 -1
  12. package/apps/agents-server/src/app/admin/files/FilesGalleryClient.tsx +4 -0
  13. package/apps/agents-server/src/app/admin/files/FilesGalleryGrid.tsx +9 -11
  14. package/apps/agents-server/src/app/admin/files/FilesGalleryTable.tsx +9 -8
  15. package/apps/agents-server/src/app/admin/images/ImagesGalleryClient.tsx +4 -0
  16. package/apps/agents-server/src/app/admin/images/ImagesGalleryGrid.tsx +16 -11
  17. package/apps/agents-server/src/app/admin/images/ImagesGalleryTable.tsx +18 -9
  18. package/apps/agents-server/src/app/admin/login-methods/shibboleth/page.tsx +9 -9
  19. package/apps/agents-server/src/app/admin/messages/MessagesClient.tsx +7 -6
  20. package/apps/agents-server/src/app/admin/servers/ServersRegistryTable.tsx +11 -10
  21. package/apps/agents-server/src/app/admin/task-manager/TaskManagerClient.tsx +4 -2
  22. package/apps/agents-server/src/app/admin/task-manager/TaskManagerTaskRow.tsx +14 -15
  23. package/apps/agents-server/src/app/admin/task-manager/TaskManagerTasksCard.tsx +4 -1
  24. package/apps/agents-server/src/app/admin/task-manager/useTaskManagerState.ts +6 -9
  25. package/apps/agents-server/src/app/admin/update/CustomCommitPicker.tsx +258 -0
  26. package/apps/agents-server/src/app/admin/update/UpdateClient.tsx +305 -108
  27. package/apps/agents-server/src/app/admin/usage/UsageClient.tsx +4 -2
  28. package/apps/agents-server/src/app/admin/usage/UsageClientAnalyticsPanels.tsx +6 -4
  29. package/apps/agents-server/src/app/admin/usage/UsageClientFormatting.ts +6 -12
  30. package/apps/agents-server/src/app/admin/usage/UsageClientTimelineChart.tsx +6 -3
  31. package/apps/agents-server/src/app/admin/usage/useUsageClientState.ts +12 -4
  32. package/apps/agents-server/src/app/admin/users/[userId]/UserDetailClient.tsx +8 -3
  33. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +6 -20
  34. package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +2 -0
  35. package/apps/agents-server/src/app/agents/[agentName]/book/useBookEditorHistory.ts +9 -2
  36. package/apps/agents-server/src/app/agents/[agentName]/chat/useAgentChatHistoryClientState.ts +2 -15
  37. package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +6 -1
  38. package/apps/agents-server/src/app/agents/[agentName]/integration/CalendarIntegrationSection.tsx +23 -5
  39. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsClient.tsx +4 -2
  40. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsFiltersCard.tsx +9 -4
  41. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsTableCard.tsx +4 -1
  42. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsTableRow.tsx +10 -3
  43. package/apps/agents-server/src/app/api/admin/update/commits/route.ts +35 -0
  44. package/apps/agents-server/src/app/api/admin/update/route.ts +10 -2
  45. package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +5 -0
  46. package/apps/agents-server/src/app/api/internal/user-chat-jobs/run/route.ts +14 -2
  47. package/apps/agents-server/src/app/api/v1/agents/[agentId]/route.ts +4 -0
  48. package/apps/agents-server/src/app/api/v1/agents/route.ts +2 -0
  49. package/apps/agents-server/src/app/dashboard/page.tsx +11 -16
  50. package/apps/agents-server/src/app/recycle-bin/actions.ts +3 -0
  51. package/apps/agents-server/src/app/swagger/SwaggerApiKeysPanel.tsx +4 -1
  52. package/apps/agents-server/src/app/system/user-memory/UserMemoryClient.tsx +4 -2
  53. package/apps/agents-server/src/app/system/user-wallet/UserWalletClient.tsx +3 -0
  54. package/apps/agents-server/src/app/system/user-wallet/UserWalletRecordsTable.tsx +5 -1
  55. package/apps/agents-server/src/app/system/utilities/mocked-chats/MockedChatsEditorClient.tsx +3 -0
  56. package/apps/agents-server/src/app/system/utilities/mocked-chats/MockedChatsEditorSidebar.tsx +7 -8
  57. package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -2
  58. package/apps/agents-server/src/components/UsersList/UsersList.tsx +4 -2
  59. package/apps/agents-server/src/database/migrations/2026-06-2600-agent-directory-performance-indexes.sql +14 -0
  60. package/apps/agents-server/src/database/sqlite/$provideLocalSqliteSupabase.ts +92 -1
  61. package/apps/agents-server/src/tools/createAgentProgressTools.ts +8 -6
  62. package/apps/agents-server/src/utils/localization/formatServerLanguageHumanReadableDate.ts +70 -0
  63. package/apps/agents-server/src/utils/localization/getRequestServerLanguage.ts +32 -0
  64. package/apps/agents-server/src/utils/userChat/createRunUserChatJobExecutionContext.ts +17 -0
  65. package/apps/agents-server/src/utils/userChat/createRunUserChatJobPersistenceController.ts +0 -21
  66. package/apps/agents-server/src/utils/userChat/listUserChats.ts +5 -1
  67. package/apps/agents-server/src/utils/userChat/persistUserChatJobProgressCard.ts +31 -0
  68. package/apps/agents-server/src/utils/userChat/retryUserChatJob.ts +0 -4
  69. package/apps/agents-server/src/utils/userChat/runUserChatJob.ts +29 -0
  70. package/apps/agents-server/src/utils/userChat/userChatMessageLifecycle.ts +2 -2
  71. package/apps/agents-server/src/utils/userChat/userChatProgressCard.ts +99 -401
  72. package/apps/agents-server/src/utils/vpsSelfUpdate.ts +625 -51
  73. package/esm/index.es.js +252 -41
  74. package/esm/index.es.js.map +1 -1
  75. package/esm/scripts/run-codex-prompts/common/buildCoderRunProgressSnapshot.d.ts +5 -1
  76. package/esm/scripts/run-codex-prompts/common/cliProgressDisplay.d.ts +7 -0
  77. package/esm/scripts/run-codex-prompts/common/coderRunEstimateCache.d.ts +31 -0
  78. package/esm/scripts/run-codex-prompts/common/progressFormatting.d.ts +2 -0
  79. package/esm/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeRunner.d.ts +3 -1
  80. package/esm/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +7 -0
  81. package/esm/src/book-3.0/cliAgentEnv.d.ts +1 -1
  82. package/esm/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
  83. package/esm/src/version.d.ts +1 -1
  84. package/package.json +1 -1
  85. package/src/book-3.0/cliAgentEnv.ts +1 -1
  86. package/src/cli/cli-commands/agents-server/startAgentsServer.ts +1 -1
  87. package/src/cli/cli-commands/coder/ThinkingLevel.ts +3 -1
  88. package/src/other/templates/getTemplatesPipelineCollection.ts +911 -741
  89. package/src/version.ts +2 -2
  90. package/src/versions.txt +2 -0
  91. package/umd/index.umd.js +254 -44
  92. package/umd/index.umd.js.map +1 -1
  93. package/umd/scripts/run-codex-prompts/common/buildCoderRunProgressSnapshot.d.ts +5 -1
  94. package/umd/scripts/run-codex-prompts/common/cliProgressDisplay.d.ts +7 -0
  95. package/umd/scripts/run-codex-prompts/common/coderRunEstimateCache.d.ts +31 -0
  96. package/umd/scripts/run-codex-prompts/common/progressFormatting.d.ts +2 -0
  97. package/umd/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeRunner.d.ts +3 -1
  98. package/umd/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +7 -0
  99. package/umd/src/book-3.0/cliAgentEnv.d.ts +1 -1
  100. package/umd/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
  101. package/umd/src/version.d.ts +1 -1
@@ -12,6 +12,7 @@ import { NextResponse } from 'next/server';
12
12
  import { buildAgentNameOrIdFilter } from '@/src/utils/agentIdentifier';
13
13
  import { getCurrentUser } from '@/src/utils/getCurrentUser';
14
14
  import { resolveServerAgentContext } from '@/src/utils/resolveServerAgentContext';
15
+ import { invalidateCachedActiveOrganizationSnapshots } from '@/src/utils/agentOrganization/loadAgentOrganizationState';
15
16
 
16
17
  /**
17
18
  * Handles patch.
@@ -45,6 +46,7 @@ export async function PATCH(request: Request, { params }: { params: Promise<{ ag
45
46
  localServerUrl: new URL(request.url).origin,
46
47
  fallbackResolver: baseAgentReferenceResolver,
47
48
  });
49
+ invalidateCachedActiveOrganizationSnapshots();
48
50
 
49
51
  return NextResponse.json({
50
52
  success: true,
@@ -74,6 +76,8 @@ export async function PATCH(request: Request, { params }: { params: Promise<{ ag
74
76
  return NextResponse.json({ success: false, error: updateResult.error.message }, { status: 500 });
75
77
  }
76
78
 
79
+ invalidateCachedActiveOrganizationSnapshots();
80
+
77
81
  return NextResponse.json({ success: true });
78
82
  } catch (error) {
79
83
  return NextResponse.json(
@@ -97,6 +101,7 @@ export async function DELETE(request: Request, { params }: { params: Promise<{ a
97
101
 
98
102
  const agentId = await collection.getAgentPermanentId(agentName);
99
103
  await collection.deleteAgent(agentId);
104
+ invalidateCachedActiveOrganizationSnapshots();
100
105
  return NextResponse.json({ success: true });
101
106
  } catch (error) {
102
107
  return NextResponse.json(
@@ -1,4 +1,4 @@
1
- import { processNextLocalUserChatJob } from '@/src/utils/localChatRunner';
1
+ import { processNextLocalUserChatJob, type ProcessLocalUserChatJobResult } from '@/src/utils/localChatRunner';
2
2
  import {
3
3
  recoverExpiredRunningUserChatJobs,
4
4
  resolveUserChatWorkerInternalToken,
@@ -51,7 +51,7 @@ async function handleUserChatJobWorkerRequest(request: Request) {
51
51
  await recoverExpiredRunningUserChatJobs();
52
52
 
53
53
  const processedJob = await processNextLocalUserChatJob({ preferredJobId });
54
- if (!processedJob) {
54
+ if (!processedJob || !shouldRequeueUserChatJobWorker(processedJob)) {
55
55
  return new Response(null, { status: 204 });
56
56
  }
57
57
 
@@ -73,6 +73,18 @@ async function handleUserChatJobWorkerRequest(request: Request) {
73
73
  }
74
74
  }
75
75
 
76
+ /**
77
+ * Decides whether one worker outcome should immediately schedule another worker tick.
78
+ *
79
+ * Mutating outcomes may have exposed more queued work, while non-mutating `waiting`
80
+ * outcomes need the foreground fallback poll instead of a tight self-requeue loop.
81
+ *
82
+ * @private route helper
83
+ */
84
+ function shouldRequeueUserChatJobWorker(processedJob: ProcessLocalUserChatJobResult): boolean {
85
+ return processedJob.didMutate;
86
+ }
87
+
76
88
  /**
77
89
  * Validates the internal worker token.
78
90
  *
@@ -6,6 +6,7 @@ import { $getTableName } from '@/src/database/$getTableName';
6
6
  import { $provideSupabaseForServer } from '@/src/database/$provideSupabaseForServer';
7
7
  import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
8
8
  import { $provideServer } from '@/src/tools/$provideServer';
9
+ import { invalidateCachedActiveOrganizationSnapshots } from '@/src/utils/agentOrganization/loadAgentOrganizationState';
9
10
  import { findOwnedAgentByIdentifier, findOwnedFolderById, type OwnedAgentRow } from '@/src/utils/agentOwnership';
10
11
  import {
11
12
  getNextOwnedAgentSortOrder,
@@ -155,6 +156,8 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
155
156
  return metadataUpdateResponse;
156
157
  }
157
158
 
159
+ invalidateCachedActiveOrganizationSnapshots();
160
+
158
161
  return createUpdatedOwnedAgentResponse(request, identityResult.identity.userId, existingAgent);
159
162
  } catch (error) {
160
163
  return mapOwnedAgentLookupErrorToResponse(request, error);
@@ -192,6 +195,7 @@ export async function DELETE(request: NextRequest, { params }: { params: Promise
192
195
 
193
196
  const collection = await $provideAgentCollectionForServer();
194
197
  await collection.deleteAgent(existingAgent.permanentId || existingAgent.agentName);
198
+ invalidateCachedActiveOrganizationSnapshots();
195
199
 
196
200
  return createManagementApiJsonResponse(request, { success: true });
197
201
  } catch (error) {
@@ -4,6 +4,7 @@ import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentColle
4
4
  import { $provideServer } from '@/src/tools/$provideServer';
5
5
  import { findOwnedFolderById, findOwnedAgentByIdentifier } from '@/src/utils/agentOwnership';
6
6
  import { createAgentWithDefaultVisibility } from '@/src/utils/createAgentWithDefaultVisibility';
7
+ import { invalidateCachedActiveOrganizationSnapshots } from '@/src/utils/agentOrganization/loadAgentOrganizationState';
7
8
  import {
8
9
  searchOwnedAgents,
9
10
  getNextOwnedAgentSortOrder,
@@ -141,6 +142,7 @@ export async function POST(request: NextRequest) {
141
142
  sortOrder,
142
143
  userId: identityResult.identity.userId,
143
144
  });
145
+ invalidateCachedActiveOrganizationSnapshots();
144
146
  const persistedRow = await findOwnedAgentByIdentifier(identityResult.identity.userId, createdAgent.permanentId);
145
147
 
146
148
  if (!persistedRow) {
@@ -4,7 +4,6 @@
4
4
  export const dynamic = 'force-dynamic';
5
5
 
6
6
  import { getSingleLlmExecutionTools } from '@promptbook-local/core';
7
- import moment from 'moment';
8
7
  import { headers } from 'next/headers';
9
8
  import { AboutPromptbookInformation } from '../../../../../src/utils/misc/xAboutPromptbookInformation';
10
9
  import { HomepagePrimarySections } from '../../components/Homepage/HomepagePrimarySections';
@@ -17,21 +16,10 @@ import { getLongRunningTask } from '../../deamons/longRunningTask';
17
16
  import { $provideExecutionToolsForServer } from '../../tools/$provideExecutionToolsForServer';
18
17
  import { $provideServer } from '../../tools/$provideServer';
19
18
  import { isUserAdmin } from '../../utils/isUserAdmin';
19
+ import { formatServerLanguageHumanReadableDate } from '../../utils/localization/formatServerLanguageHumanReadableDate';
20
+ import { getRequestServerLanguage } from '../../utils/localization/getRequestServerLanguage';
20
21
  import { getHomePageAgents } from '../_data/getHomePageAgents';
21
22
 
22
- // Add calendar formats that include seconds
23
- /**
24
- * Map of calendar with seconds.
25
- */
26
- const calendarWithSeconds = {
27
- sameDay: '[Today at] LTS',
28
- nextDay: '[Tomorrow at] LTS',
29
- nextWeek: 'dddd [at] LTS',
30
- lastDay: '[Yesterday at] LTS',
31
- lastWeek: '[Last] dddd [at] LTS',
32
- sameElse: 'L [at] LTS',
33
- };
34
-
35
23
  /**
36
24
  * Renders the legacy dashboard with system and admin details.
37
25
  */
@@ -40,6 +28,7 @@ export default async function DashboardPage() {
40
28
  const { publicUrl } = server;
41
29
  const isAdmin = await isUserAdmin(); /* <- TODO: [??] Here should be user permissions */
42
30
  const { agents, folders, homepageMessage, currentUser } = await getHomePageAgents();
31
+ const language = await getRequestServerLanguage();
43
32
 
44
33
  const longRunningTask = getLongRunningTask();
45
34
 
@@ -92,10 +81,16 @@ export default async function DashboardPage() {
92
81
  <TechInfoCard title={`Long running task ${longRunningTask.taskId}`}>
93
82
  <p className="text-gray-600">Tick: {longRunningTask.tick}</p>
94
83
  <p className="text-gray-600">
95
- Created At: {moment(longRunningTask.createdAt).calendar(undefined, calendarWithSeconds)}
84
+ Created At:{' '}
85
+ {formatServerLanguageHumanReadableDate(longRunningTask.createdAt, language, {
86
+ isExactDateIncluded: true,
87
+ })}
96
88
  </p>
97
89
  <p className="text-gray-600">
98
- Updated At: {moment(longRunningTask.updatedAt).calendar(undefined, calendarWithSeconds)}
90
+ Updated At:{' '}
91
+ {formatServerLanguageHumanReadableDate(longRunningTask.updatedAt, language, {
92
+ isExactDateIncluded: true,
93
+ })}
99
94
  </p>
100
95
  </TechInfoCard>
101
96
 
@@ -4,6 +4,7 @@ import { restoreAgentAndFolders } from '@/src/utils/agentOrganization/restoreAge
4
4
  import { revalidatePath } from 'next/cache';
5
5
  import { isUserAdmin } from '../../utils/isUserAdmin';
6
6
  import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
7
+ import { invalidateCachedActiveOrganizationSnapshots } from '@/src/utils/agentOrganization/loadAgentOrganizationState';
7
8
 
8
9
  /**
9
10
  * Restores a deleted agent from the recycle bin.
@@ -16,6 +17,7 @@ export async function restoreDeletedAgent(agentName: string) {
16
17
  }
17
18
 
18
19
  await restoreAgentAndFolders(agentName);
20
+ invalidateCachedActiveOrganizationSnapshots();
19
21
 
20
22
  revalidatePath('/recycle-bin');
21
23
  revalidatePath(`/agents/${agentName}`);
@@ -35,6 +37,7 @@ export async function deleteAgent(agentName: string) {
35
37
  const collection = await $provideAgentCollectionForServer();
36
38
 
37
39
  await collection.deleteAgent(agentName);
40
+ invalidateCachedActiveOrganizationSnapshots();
38
41
 
39
42
  revalidatePath('/recycle-bin');
40
43
  revalidatePath(`/agents/${agentName}`);
@@ -3,9 +3,11 @@
3
3
  import Link from 'next/link';
4
4
  import { Plus } from 'lucide-react';
5
5
  import { useEffect, useState } from 'react';
6
+ import { useServerLanguage } from '@/src/components/ServerLanguage/ServerLanguageProvider';
6
7
  import { SecretInput } from '@/src/components/SecretInput/SecretInput';
7
8
  import type { ApiTokenEntry } from '@/src/utils/apiTokensClient';
8
9
  import { createApiToken, fetchApiTokens } from '@/src/utils/apiTokensClient';
10
+ import { formatServerLanguageHumanReadableDate } from '@/src/utils/localization/formatServerLanguageHumanReadableDate';
9
11
 
10
12
  /**
11
13
  * Props for the compact Swagger API-key panel.
@@ -21,6 +23,7 @@ type SwaggerApiKeysPanelProps = {
21
23
  * Compact API-key panel shown above Swagger UI.
22
24
  */
23
25
  export function SwaggerApiKeysPanel({ isAdmin }: SwaggerApiKeysPanelProps) {
26
+ const { language } = useServerLanguage();
24
27
  const [tokens, setTokens] = useState<ApiTokenEntry[]>([]);
25
28
  const [isLoading, setIsLoading] = useState(isAdmin);
26
29
  const [isCreating, setIsCreating] = useState(false);
@@ -110,7 +113,7 @@ export function SwaggerApiKeysPanel({ isAdmin }: SwaggerApiKeysPanelProps) {
110
113
  {token.note || `API key #${token.id}`}
111
114
  </span>
112
115
  <span className="text-xs text-slate-400">
113
- {new Date(token.createdAt).toLocaleString()}
116
+ {formatServerLanguageHumanReadableDate(token.createdAt, language)}
114
117
  </span>
115
118
  </div>
116
119
  <SecretInput value={token.token} readOnly aria-label={`API key ${token.id}`} />
@@ -1,6 +1,8 @@
1
1
  'use client';
2
2
 
3
3
  import { showConfirm } from '@/src/components/AsyncDialogs/asyncDialogs';
4
+ import { useServerLanguage } from '@/src/components/ServerLanguage/ServerLanguageProvider';
5
+ import { formatServerLanguageHumanReadableDate } from '@/src/utils/localization/formatServerLanguageHumanReadableDate';
4
6
  import { useEffect, useMemo, useState } from 'react';
5
7
 
6
8
  /**
@@ -42,6 +44,7 @@ type UserMemoryClientProps = {
42
44
  */
43
45
  export function UserMemoryClient(props: UserMemoryClientProps) {
44
46
  const { agents } = props;
47
+ const { language } = useServerLanguage();
45
48
  const [memories, setMemories] = useState<UserMemoryEntry[]>([]);
46
49
  const [loading, setLoading] = useState<boolean>(true);
47
50
  const [saving, setSaving] = useState<boolean>(false);
@@ -445,7 +448,7 @@ export function UserMemoryClient(props: UserMemoryClientProps) {
445
448
  )}
446
449
  </td>
447
450
  <td className="px-4 py-3 text-xs text-gray-500">
448
- {new Date(memory.updatedAt).toLocaleString()}
451
+ {formatServerLanguageHumanReadableDate(memory.updatedAt, language)}
449
452
  </td>
450
453
  <td className="px-4 py-3 text-right text-sm">
451
454
  {isEditing ? (
@@ -491,4 +494,3 @@ export function UserMemoryClient(props: UserMemoryClientProps) {
491
494
  </div>
492
495
  );
493
496
  }
494
-
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { WalletRecordDialog } from '@/src/components/WalletRecordDialog/WalletRecordDialog';
4
+ import { useServerLanguage } from '@/src/components/ServerLanguage/ServerLanguageProvider';
4
5
  import type { GithubAppStatusResponse } from '@/src/utils/githubAppClient';
5
6
  import { UserWalletCreateCard } from './UserWalletCreateCard';
6
7
  import type { UserWalletAgentOption } from './UserWalletAgentOption';
@@ -34,6 +35,7 @@ function resolveGithubAppStatusDescription(status: GithubAppStatusResponse | nul
34
35
  */
35
36
  export function UserWalletClient(props: UserWalletClientProps) {
36
37
  const { agents } = props;
38
+ const { language } = useServerLanguage();
37
39
  const state = useUserWalletClientState({ agents });
38
40
 
39
41
  return (
@@ -118,6 +120,7 @@ export function UserWalletClient(props: UserWalletClientProps) {
118
120
 
119
121
  <UserWalletRecordsTable
120
122
  agents={agents}
123
+ language={language}
121
124
  editingDraft={state.editingDraft}
122
125
  isEditingSmtpRecord={state.isEditingSmtpRecord}
123
126
  isLoading={state.loading}
@@ -1,6 +1,8 @@
1
1
  'use client';
2
2
 
3
3
  import type { UserWalletRecord } from '@/src/utils/userWallet';
4
+ import type { ServerLanguageCode } from '@/src/languages/ServerLanguageRegistry';
5
+ import { formatServerLanguageHumanReadableDate } from '@/src/utils/localization/formatServerLanguageHumanReadableDate';
4
6
  import type { UserWalletAgentOption } from './UserWalletAgentOption';
5
7
  import { formatWalletJsonSchemaForTextarea, type EditingUserWalletDraft, type UpdateUserWalletDraft } from './UserWalletDraft';
6
8
  import { UserWalletRecordFormFields } from './UserWalletRecordFormFields';
@@ -11,6 +13,7 @@ import { UserWalletRecordValue } from './UserWalletRecordValue';
11
13
  */
12
14
  type UserWalletRecordsTableProps = {
13
15
  agents: ReadonlyArray<UserWalletAgentOption>;
16
+ language: ServerLanguageCode;
14
17
  editingDraft: EditingUserWalletDraft | null;
15
18
  isEditingSmtpRecord: boolean;
16
19
  isLoading: boolean;
@@ -31,6 +34,7 @@ type UserWalletRecordsTableProps = {
31
34
  */
32
35
  export function UserWalletRecordsTable({
33
36
  agents,
37
+ language,
34
38
  editingDraft,
35
39
  isEditingSmtpRecord,
36
40
  isLoading,
@@ -118,7 +122,7 @@ export function UserWalletRecordsTable({
118
122
  )}
119
123
  </td>
120
124
  <td className="px-4 py-3 text-xs text-gray-500">
121
- {new Date(record.updatedAt).toLocaleString()}
125
+ {formatServerLanguageHumanReadableDate(record.updatedAt, language)}
122
126
  </td>
123
127
  <td className="px-4 py-3 text-right text-sm">
124
128
  {isEditing ? (
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useEffect, useMemo, useState } from 'react';
4
+ import { useServerLanguage } from '@/src/components/ServerLanguage/ServerLanguageProvider';
4
5
  import type { MockedChatPreset } from '@/src/utils/mockedChatsSchema';
5
6
  import { MockedChatsEditorForm } from './MockedChatsEditorForm';
6
7
  import { MockedChatsEditorSidebar } from './MockedChatsEditorSidebar';
@@ -17,6 +18,7 @@ type MockedChatsEditorClientProps = {
17
18
  * Mocked chat editor under `System -> Utilities`.
18
19
  */
19
20
  export function MockedChatsEditorClient({ initialMockedChats }: MockedChatsEditorClientProps) {
21
+ const { language } = useServerLanguage();
20
22
  const mockedChatsEditorState = useMockedChatsEditorState({ initialMockedChats });
21
23
  const viewerHref = useMemo(
22
24
  () => buildMockedChatViewerHref(mockedChatsEditorState.draftChat.id),
@@ -47,6 +49,7 @@ export function MockedChatsEditorClient({ initialMockedChats }: MockedChatsEdito
47
49
 
48
50
  <div className="grid gap-6 lg:grid-cols-[340px_minmax(0,1fr)]">
49
51
  <MockedChatsEditorSidebar
52
+ language={language}
50
53
  savedChats={mockedChatsEditorState.savedChats}
51
54
  selectedChatId={mockedChatsEditorState.selectedChatId}
52
55
  createNewDraft={mockedChatsEditorState.createNewDraft}
@@ -1,4 +1,6 @@
1
1
  import Link from 'next/link';
2
+ import type { ServerLanguageCode } from '@/src/languages/ServerLanguageRegistry';
3
+ import { formatServerLanguageHumanReadableDate } from '@/src/utils/localization/formatServerLanguageHumanReadableDate';
2
4
  import type { UseMockedChatsEditorState } from './useMockedChatsEditorState';
3
5
 
4
6
  /**
@@ -8,6 +10,7 @@ type MockedChatsEditorSidebarProps = Pick<
8
10
  UseMockedChatsEditorState,
9
11
  'savedChats' | 'selectedChatId' | 'createNewDraft' | 'selectSavedChat'
10
12
  > & {
13
+ language: ServerLanguageCode;
11
14
  buildViewerHref: (mockedChatId: string) => string;
12
15
  };
13
16
 
@@ -16,13 +19,8 @@ type MockedChatsEditorSidebarProps = Pick<
16
19
  *
17
20
  * @private function of <MockedChatsEditorClient/>
18
21
  */
19
- function formatMockedChatUpdatedAt(updatedAt: string): string {
20
- const date = new Date(updatedAt);
21
- if (Number.isNaN(date.getTime())) {
22
- return 'just now';
23
- }
24
-
25
- return date.toLocaleString();
22
+ function formatMockedChatUpdatedAt(updatedAt: string, language: ServerLanguageCode): string {
23
+ return formatServerLanguageHumanReadableDate(updatedAt, language, { fallbackLabel: 'just now' });
26
24
  }
27
25
 
28
26
  /**
@@ -31,6 +29,7 @@ function formatMockedChatUpdatedAt(updatedAt: string): string {
31
29
  * @private function of <MockedChatsEditorClient/>
32
30
  */
33
31
  export function MockedChatsEditorSidebar({
32
+ language,
34
33
  savedChats,
35
34
  selectedChatId,
36
35
  createNewDraft,
@@ -74,7 +73,7 @@ export function MockedChatsEditorSidebar({
74
73
  <div className="min-w-0">
75
74
  <p className="truncate text-sm font-semibold text-slate-900">{chat.name}</p>
76
75
  <p className="mt-1 text-xs text-slate-500">
77
- Updated {formatMockedChatUpdatedAt(chat.updatedAt)}
76
+ Updated {formatMockedChatUpdatedAt(chat.updatedAt, language)}
78
77
  </p>
79
78
  </div>
80
79
  <Link
@@ -1,5 +1,7 @@
1
1
  import { formatAgentNamingText } from '../../utils/agentNaming';
2
2
  import { getAgentNaming } from '../../utils/getAgentNaming';
3
+ import { formatServerLanguageHumanReadableDate } from '../../utils/localization/formatServerLanguageHumanReadableDate';
4
+ import { getRequestServerLanguage } from '../../utils/localization/getRequestServerLanguage';
3
5
 
4
6
  /**
5
7
  * Renders a print-only header with server branding.
@@ -8,7 +10,7 @@ import { getAgentNaming } from '../../utils/getAgentNaming';
8
10
  * @returns Print header markup.
9
11
  */
10
12
  export async function PrintHeader({ title }: { title?: string }) {
11
- const agentNaming = await getAgentNaming();
13
+ const [agentNaming, language] = await Promise.all([getAgentNaming(), getRequestServerLanguage()]);
12
14
  return (
13
15
  <div className="hidden print:block mb-6 border-b-2 border-blue-600 pb-2">
14
16
  <div className="flex justify-between items-end">
@@ -23,7 +25,7 @@ export async function PrintHeader({ title }: { title?: string }) {
23
25
  {title && <h2 className="text-lg font-semibold text-gray-700">{title}</h2>}
24
26
  </div>
25
27
  <div className="text-xs text-gray-400 mt-1 text-right">
26
- {new Date().toLocaleDateString()}
28
+ {formatServerLanguageHumanReadableDate(new Date(), language, { isExactDateIncluded: true })}
27
29
  </div>
28
30
  </div>
29
31
  );
@@ -6,6 +6,7 @@ import { Card } from '../Homepage/Card';
6
6
  import { Section } from '../Homepage/Section';
7
7
  import { useServerLanguage } from '../ServerLanguage/ServerLanguageProvider';
8
8
  import { SecretInput } from '@/src/components/SecretInput/SecretInput';
9
+ import { formatServerLanguageHumanReadableDate } from '@/src/utils/localization/formatServerLanguageHumanReadableDate';
9
10
  import { useUsersAdmin } from './useUsersAdmin';
10
11
 
11
12
  /**
@@ -26,7 +27,7 @@ type UsersListProps = {
26
27
  */
27
28
  export function UsersList({ allowCreate = true }: UsersListProps) {
28
29
  const { users, loading, error, createUser, deleteUser, toggleAdmin } = useUsersAdmin();
29
- const { t } = useServerLanguage();
30
+ const { language, t } = useServerLanguage();
30
31
 
31
32
  const [newUsername, setNewUsername] = useState('');
32
33
  const [newPassword, setNewPassword] = useState('');
@@ -91,7 +92,8 @@ export function UsersList({ allowCreate = true }: UsersListProps) {
91
92
  </p>
92
93
  )}
93
94
  <p className="text-gray-500 text-sm mt-2">
94
- {t('users.createdLabel')}: {new Date(user.createdAt).toLocaleDateString()}
95
+ {t('users.createdLabel')}:{' '}
96
+ {formatServerLanguageHumanReadableDate(user.createdAt, language)}
95
97
  </p>
96
98
  </div>
97
99
  <div className="space-x-2">
@@ -0,0 +1,14 @@
1
+ CREATE INDEX IF NOT EXISTS "prefix_Agent_deletedAt_sortOrder_agentName_idx"
2
+ ON "prefix_Agent" ("deletedAt", "sortOrder", "agentName");
3
+
4
+ CREATE INDEX IF NOT EXISTS "prefix_Agent_visibility_deletedAt_sortOrder_agentName_idx"
5
+ ON "prefix_Agent" ("visibility", "deletedAt", "sortOrder", "agentName");
6
+
7
+ CREATE INDEX IF NOT EXISTS "prefix_AgentFolder_deletedAt_parentId_sortOrder_name_idx"
8
+ ON "prefix_AgentFolder" ("deletedAt", "parentId", "sortOrder", "name");
9
+
10
+ CREATE INDEX IF NOT EXISTS "prefix_UserChat_userId_agentPermanentId_source_createdAt_idx"
11
+ ON "prefix_UserChat" ("userId", "agentPermanentId", "source", "createdAt" DESC);
12
+
13
+ CREATE INDEX IF NOT EXISTS "prefix_UserChat_agentPermanentId_source_userId_createdAt_idx"
14
+ ON "prefix_UserChat" ("agentPermanentId", "source", "userId", "createdAt" DESC);
@@ -65,6 +65,14 @@ type LocalSqliteUpsertOptions = {
65
65
  readonly onConflict?: string;
66
66
  };
67
67
 
68
+ /**
69
+ * Read index definition for hot SQLite queries.
70
+ */
71
+ type LocalSqliteReadIndex = {
72
+ readonly name: string;
73
+ readonly columns: ReadonlyArray<string>;
74
+ };
75
+
68
76
  /**
69
77
  * Columns whose values are persisted as JSON text in local SQLite.
70
78
  */
@@ -141,6 +149,51 @@ const UNIQUE_INDEX_COLUMNS_BY_TABLE = new Map<string, ReadonlyArray<ReadonlyArra
141
149
  ['UserPushSubscription', [['endpoint']]],
142
150
  ]);
143
151
 
152
+ /**
153
+ * Non-unique indexes for frequent standalone VPS reads.
154
+ */
155
+ const READ_INDEXES_BY_TABLE = new Map<string, ReadonlyArray<LocalSqliteReadIndex>>([
156
+ [
157
+ 'Agent',
158
+ [
159
+ { name: 'agentName_lookup', columns: ['agentName'] },
160
+ { name: 'active_directory', columns: ['deletedAt', 'sortOrder', 'agentName'] },
161
+ { name: 'public_active_directory', columns: ['visibility', 'deletedAt', 'sortOrder', 'agentName'] },
162
+ ],
163
+ ],
164
+ [
165
+ 'AgentFolder',
166
+ [{ name: 'active_directory', columns: ['deletedAt', 'parentId', 'sortOrder', 'name'] }],
167
+ ],
168
+ [
169
+ 'UserChat',
170
+ [
171
+ { name: 'user_agent_source_createdAt', columns: ['userId', 'agentPermanentId', 'source', 'createdAt'] },
172
+ { name: 'agent_source_user_createdAt', columns: ['agentPermanentId', 'source', 'userId', 'createdAt'] },
173
+ ],
174
+ ],
175
+ [
176
+ 'UserChatJob',
177
+ [
178
+ { name: 'ready_queue', columns: ['status', 'cancelRequestedAt', 'queuedAt', 'createdAt'] },
179
+ { name: 'active_chat_scope', columns: ['chatId', 'userId', 'agentPermanentId', 'status', 'createdAt'] },
180
+ { name: 'agent_chat_status', columns: ['agentPermanentId', 'chatId', 'status'] },
181
+ { name: 'running_lease', columns: ['status', 'leaseExpiresAt'] },
182
+ ],
183
+ ],
184
+ [
185
+ 'UserChatTimeout',
186
+ [
187
+ { name: 'ready_due', columns: ['status', 'cancelRequestedAt', 'pausedAt', 'dueAt', 'createdAt'] },
188
+ {
189
+ name: 'active_chat_scope',
190
+ columns: ['chatId', 'userId', 'agentPermanentId', 'status', 'pausedAt', 'dueAt', 'createdAt'],
191
+ },
192
+ { name: 'running_lease', columns: ['status', 'leaseExpiresAt'] },
193
+ ],
194
+ ],
195
+ ]);
196
+
144
197
  /**
145
198
  * Known unique conflict columns used when `.upsert` omits `onConflict`.
146
199
  */
@@ -179,6 +232,20 @@ export function $resetLocalSqliteSupabaseForTests(): void {
179
232
  localSqliteSupabase = null;
180
233
  }
181
234
 
235
+ /**
236
+ * Ensures read indexes for a table that is queried through direct SQLite SQL.
237
+ *
238
+ * @param tableName - Actual table name, including any server prefix.
239
+ *
240
+ * @private internal SQLite utility of Agents Server
241
+ */
242
+ export function ensureLocalSqliteTableReadIndexes(tableName: string): void {
243
+ const database = $provideAgentsServerSqliteDatabase();
244
+ const tableBaseName = resolveTableBaseName(tableName);
245
+
246
+ ensureTable(database, tableName, resolveReadIndexColumns(tableBaseName));
247
+ }
248
+
182
249
  /**
183
250
  * Supabase-shaped client with only the table query surface used by Agents Server.
184
251
  */
@@ -836,8 +903,9 @@ function ensureTable(
836
903
  const columnsToEnsure = uniqueStrings([...requiredColumns, ...resolveUniqueIndexColumns(tableBaseName)]).filter(
837
904
  (column) => column !== '*' && column !== 'id',
838
905
  );
906
+ const columnsToEnsureWithIndexes = uniqueStrings([...columnsToEnsure, ...resolveReadIndexColumns(tableBaseName)]);
839
907
 
840
- for (const column of columnsToEnsure) {
908
+ for (const column of columnsToEnsureWithIndexes) {
841
909
  if (existingColumns.has(column)) {
842
910
  continue;
843
911
  }
@@ -852,6 +920,7 @@ function ensureTable(
852
920
  }
853
921
 
854
922
  ensureUniqueIndexes(database, tableName, tableBaseName);
923
+ ensureReadIndexes(database, tableName, tableBaseName);
855
924
  }
856
925
 
857
926
  /**
@@ -871,6 +940,21 @@ function ensureUniqueIndexes(database: AgentsServerSqliteDatabase, tableName: st
871
940
  }
872
941
  }
873
942
 
943
+ /**
944
+ * Creates known read indexes after required columns exist.
945
+ */
946
+ function ensureReadIndexes(database: AgentsServerSqliteDatabase, tableName: string, tableBaseName: string): void {
947
+ const readIndexes = READ_INDEXES_BY_TABLE.get(tableBaseName) || [];
948
+
949
+ for (const readIndex of readIndexes) {
950
+ const indexName = `index_${sanitizeSqlIdentifier(tableName)}_${sanitizeSqlIdentifier(readIndex.name)}`;
951
+ const columnSql = readIndex.columns.map(quoteIdentifier).join(', ');
952
+ database.exec(
953
+ `CREATE INDEX IF NOT EXISTS ${quoteIdentifier(indexName)} ON ${quoteIdentifier(tableName)} (${columnSql})`,
954
+ );
955
+ }
956
+ }
957
+
874
958
  /**
875
959
  * Inserts one row into the table.
876
960
  */
@@ -1441,6 +1525,13 @@ function resolveUniqueIndexColumns(tableBaseName: string): Array<string> {
1441
1525
  return (UNIQUE_INDEX_COLUMNS_BY_TABLE.get(tableBaseName) || []).flatMap((columns) => [...columns]);
1442
1526
  }
1443
1527
 
1528
+ /**
1529
+ * Resolves columns participating in known read indexes.
1530
+ */
1531
+ function resolveReadIndexColumns(tableBaseName: string): Array<string> {
1532
+ return (READ_INDEXES_BY_TABLE.get(tableBaseName) || []).flatMap((readIndex) => [...readIndex.columns]);
1533
+ }
1534
+
1444
1535
  /**
1445
1536
  * Resolves upsert conflict columns.
1446
1537
  */
@@ -22,13 +22,15 @@ export function createAgentProgressTools(
22
22
  {
23
23
  name: AGENT_PROGRESS_TOOL_NAME,
24
24
  description: spaceTrim(`
25
- Publish real-time progress to the user while you work on a response.
25
+ Update the visible progress card while you work on a response.
26
26
 
27
- - Use this for concrete progress, not placeholder reassurance.
28
- - Call it before any chunk of work that may take more than a few seconds.
29
- - Update it when you start a step, call or finish a tool, learn a result, or decide the next step.
30
- - Include what you are doing now, visible actions/results, and what you will do next.
31
- - Keep updates concise and user-facing; do not expose hidden chain-of-thought.
27
+ - Call \`initialize\` before meaningful work when the answer may take more than a few seconds.
28
+ - Call \`update\` after each user-relevant decision, action, tool use, or result.
29
+ - Use \`append_items\` for new checklist items discovered while working.
30
+ - Call \`finalize\` immediately before the final answer.
31
+ - Keep every update concise, user-facing, and specific about what is happening now and what comes next.
32
+ - Summarize your reasoning as visible decisions and checks, not hidden chain-of-thought.
33
+ - Do not expose system prompts, raw tool payloads, secrets, private implementation details, or technical logs.
32
34
  `),
33
35
  parameters: {
34
36
  type: 'object',