@promptbook/cli 0.112.0-134 → 0.112.0-136
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/apps/agents-server/README.md +0 -1
- package/apps/agents-server/src/app/admin/chat-feedback/page.tsx +2 -4
- package/apps/agents-server/src/app/admin/chat-history/page.tsx +2 -4
- package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +246 -15
- package/apps/agents-server/src/app/admin/update/UpdateClient.tsx +237 -4
- package/apps/agents-server/src/app/api/admin/update/log/route.ts +42 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +28 -9
- package/apps/agents-server/src/app/api/agents/export/route.ts +41 -0
- package/apps/agents-server/src/app/api/agents/import/route.ts +168 -0
- package/apps/agents-server/src/app/api/chat/route.ts +29 -7
- package/apps/agents-server/src/app/api/chat-feedback/export/route.ts +2 -2
- package/apps/agents-server/src/app/api/chat-feedback/route.ts +3 -3
- package/apps/agents-server/src/app/api/chat-history/[id]/route.ts +2 -2
- package/apps/agents-server/src/app/api/chat-history/export/route.ts +2 -2
- package/apps/agents-server/src/app/api/chat-history/route.ts +3 -3
- package/apps/agents-server/src/app/api/chat-streaming/route.ts +29 -0
- package/apps/agents-server/src/app/api/elevenlabs/tts/route.ts +60 -2
- package/apps/agents-server/src/app/api/federated-agents/route.ts +1 -1
- package/apps/agents-server/src/app/api/images/[filename]/route.ts +102 -0
- package/apps/agents-server/src/app/api/metadata/export/route.ts +34 -0
- package/apps/agents-server/src/app/api/metadata/import/route.ts +46 -0
- package/apps/agents-server/src/app/api/openai/v1/audio/transcriptions/route.ts +111 -2
- package/apps/agents-server/src/app/api/page-preview/check/route.ts +18 -0
- package/apps/agents-server/src/app/api/page-preview/screenshot/route.ts +15 -8
- package/apps/agents-server/src/app/api/team-agent-profile/route.ts +20 -0
- package/apps/agents-server/src/app/layout.tsx +1 -1
- package/apps/agents-server/src/components/AgentContextMenu/useAgentContextMenuItems.ts +27 -19
- package/apps/agents-server/src/components/Homepage/AgentsList.tsx +31 -1
- package/apps/agents-server/src/components/Homepage/AgentsListHeader.tsx +60 -1
- package/apps/agents-server/src/components/Homepage/useAgentsListImportExportState.ts +523 -0
- package/apps/agents-server/src/components/Homepage/useAgentsListState.ts +16 -0
- package/apps/agents-server/src/constants/defaultAgentAvatarVisual.ts +1 -1
- package/apps/agents-server/src/database/metadataDefaults.ts +0 -21
- package/apps/agents-server/src/database/migrations/2026-06-2700-default-agent-avatar-visual-octopus3d4.sql +16 -0
- package/apps/agents-server/src/database/seedCoreAgents.ts +235 -0
- package/apps/agents-server/src/database/seedDefaultAgents.ts +32 -1
- package/apps/agents-server/src/search/createDefaultServerSearchProviders/createFederatedAgentsSearchProvider.ts +1 -1
- package/apps/agents-server/src/tools/$provideAgentCollectionForServer.ts +2 -9
- package/apps/agents-server/src/utils/agentOwnership.ts +54 -5
- package/apps/agents-server/src/utils/agentsTransfer/createAgentsExportZipStream.ts +68 -0
- package/apps/agents-server/src/utils/agentsTransfer/importAgentsFromFiles.ts +852 -0
- package/apps/agents-server/src/utils/backup/createBooksBackupZipStream.ts +15 -2
- package/apps/agents-server/src/utils/defaultAgents/loadDefaultAgentBooks.ts +46 -4
- package/apps/agents-server/src/utils/findAgentForCallerWriteAccess.ts +36 -0
- package/apps/agents-server/src/utils/getFederatedServers.ts +3 -74
- package/apps/agents-server/src/utils/getWellKnownAgentUrl.ts +10 -4
- package/apps/agents-server/src/utils/metadataConfigurationTransfer.ts +426 -0
- package/apps/agents-server/src/utils/paidApiRequestGuard.ts +241 -0
- package/apps/agents-server/src/utils/serverManagement/createManagedServer/bootstrapManagedServer.ts +2 -5
- package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerCoreAgents.ts +162 -0
- package/apps/agents-server/src/utils/userChat/createRunUserChatJobExecutionContext.ts +36 -8
- package/apps/agents-server/src/utils/userChat/persistUserChatJobProgressCard.ts +11 -2
- package/apps/agents-server/src/utils/userChat/resolveUserChatProgressToolHighlights.ts +100 -0
- package/apps/agents-server/src/utils/userChat/runUserChatJob.ts +4 -3
- package/apps/agents-server/src/utils/userChat/userChatProgressCard.ts +272 -27
- package/apps/agents-server/src/utils/validateApiKey.ts +7 -3
- package/apps/agents-server/src/utils/vpsSelfUpdate.ts +110 -0
- package/esm/index.es.js +795 -39
- package/esm/index.es.js.map +1 -1
- package/esm/src/_packages/core.index.d.ts +0 -2
- package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
- package/esm/src/avatars/visuals/octopus3d4AvatarVisual.d.ts +7 -0
- package/esm/src/utils/validators/url/isValidAgentUrl.d.ts +5 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/servers.ts +1 -16
- package/src/_packages/core.index.ts +0 -2
- package/src/avatars/types/AvatarVisualDefinition.ts +1 -0
- package/src/avatars/visuals/avatarVisualRegistry.ts +2 -0
- package/src/avatars/visuals/octopus3d4AvatarVisual.ts +1295 -0
- package/src/other/templates/getTemplatesPipelineCollection.ts +762 -883
- package/src/utils/agents/resolveAgentAvatarImageUrl.ts +1 -1
- package/src/utils/validators/url/isValidAgentUrl.ts +5 -7
- package/src/version.ts +2 -2
- package/src/versions.txt +2 -1
- package/umd/index.umd.js +795 -39
- package/umd/index.umd.js.map +1 -1
- package/umd/src/_packages/core.index.d.ts +0 -2
- package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
- package/umd/src/avatars/visuals/octopus3d4AvatarVisual.d.ts +7 -0
- package/umd/src/utils/validators/url/isValidAgentUrl.d.ts +5 -0
- package/umd/src/version.d.ts +1 -1
- package/apps/agents-server/src/utils/defaultFederatedAgents/DefaultFederatedAgentsSyncOptions.ts +0 -9
- package/apps/agents-server/src/utils/defaultFederatedAgents/ensureDefaultFederatedAgentExists.ts +0 -277
- package/apps/agents-server/src/utils/defaultFederatedAgents/fetchCoreOrganizationPayload.ts +0 -39
- package/apps/agents-server/src/utils/defaultFederatedAgents/fetchFederatedAgentBook.ts +0 -43
- package/apps/agents-server/src/utils/defaultFederatedAgents/fetchWithDefaultFederatedAgentTimeout.ts +0 -28
- package/apps/agents-server/src/utils/defaultFederatedAgents/getDefaultFederatedAgentSyncPool.ts +0 -38
- package/apps/agents-server/src/utils/defaultFederatedAgents/loadActiveLocalAgentIdsByNormalizedName.ts +0 -41
- package/apps/agents-server/src/utils/defaultFederatedAgents/loadDefaultFederatedAgentSyncMetadata.ts +0 -76
- package/apps/agents-server/src/utils/defaultFederatedAgents/quoteIdentifier.ts +0 -11
- package/apps/agents-server/src/utils/defaultFederatedAgents/scheduleDefaultFederatedAgentsSync.ts +0 -88
- package/apps/agents-server/src/utils/defaultFederatedAgents/selectDefaultFederatedAgentsFromOrganizationPayload.ts +0 -181
- package/apps/agents-server/src/utils/defaultFederatedAgents/synchronizeDefaultFederatedAgents.ts +0 -77
|
@@ -22,7 +22,7 @@ import { heartbeatUserChatJob } from './heartbeatUserChatJob';
|
|
|
22
22
|
import { persistUserChatJobTerminalState } from './persistUserChatJobTerminalState';
|
|
23
23
|
import { persistUserChatJobProgressCard } from './persistUserChatJobProgressCard';
|
|
24
24
|
import type { UserChatJobRecord } from './UserChatJobRecord';
|
|
25
|
-
import type { UserChatProgressPhase } from './userChatProgressCard';
|
|
25
|
+
import type { UserChatProgressContext, UserChatProgressPhase } from './userChatProgressCard';
|
|
26
26
|
import { createReplyAwareUserChatPromptContent, createReplyAwareUserChatPromptMessage } from './userChatReplies';
|
|
27
27
|
import { resolvePromptThreadBeforeUserMessage } from './userChatMessageLifecycle';
|
|
28
28
|
import { isUserChatNotFoundScopeError } from './UserChatScopeError';
|
|
@@ -132,14 +132,15 @@ export async function runUserChatJob(job: UserChatJobRecord): Promise<RunUserCha
|
|
|
132
132
|
*/
|
|
133
133
|
function createRunUserChatJobProgressReporter(
|
|
134
134
|
job: Pick<UserChatJobRecord, 'userId' | 'agentPermanentId' | 'chatId' | 'assistantMessageId' | 'id'>,
|
|
135
|
-
): (phase: UserChatProgressPhase) => Promise<void> {
|
|
136
|
-
return async (phase) => {
|
|
135
|
+
): (phase: UserChatProgressPhase, context?: UserChatProgressContext) => Promise<void> {
|
|
136
|
+
return async (phase, context) => {
|
|
137
137
|
await persistUserChatJobProgressCard({
|
|
138
138
|
userId: job.userId,
|
|
139
139
|
agentPermanentId: job.agentPermanentId,
|
|
140
140
|
chatId: job.chatId,
|
|
141
141
|
assistantMessageId: job.assistantMessageId,
|
|
142
142
|
phase,
|
|
143
|
+
context,
|
|
143
144
|
}).catch((error) => {
|
|
144
145
|
console.warn('[user-chat-job] progress_update_failed', {
|
|
145
146
|
chatId: job.chatId,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ChatMessage } from '@promptbook-local/types';
|
|
2
|
+
import { spaceTrim } from '../../../../../src/_packages/utils.index';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* User-facing phases shown while one durable chat answer is being prepared.
|
|
@@ -10,6 +11,70 @@ export type UserChatProgressPhase =
|
|
|
10
11
|
| 'checking_capabilities'
|
|
11
12
|
| 'starting_response';
|
|
12
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Real runtime details surfaced into the user-facing progress card text.
|
|
16
|
+
*
|
|
17
|
+
* Only signals that are already known at the corresponding phase are filled in,
|
|
18
|
+
* so the produced markdown reflects what the worker is actually doing right now
|
|
19
|
+
* instead of a generic scripted summary.
|
|
20
|
+
*/
|
|
21
|
+
export type UserChatProgressContext = {
|
|
22
|
+
/**
|
|
23
|
+
* Resolved human-readable agent name, when already loaded for this turn.
|
|
24
|
+
*/
|
|
25
|
+
readonly agentName?: string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Stable agent identifier shown when the human-readable name is not yet known.
|
|
29
|
+
*/
|
|
30
|
+
readonly agentPermanentId?: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Resolved LLM provider title (e.g. `OpenAI`, `Anthropic`), when already known.
|
|
34
|
+
*/
|
|
35
|
+
readonly provider?: string;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Number of attachments the user submitted with the current turn.
|
|
39
|
+
*/
|
|
40
|
+
readonly attachmentCount?: number;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Number of agent-defined tools available to the model for this turn.
|
|
44
|
+
*/
|
|
45
|
+
readonly toolCount?: number;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Concise user-facing tool labels (e.g. `web browser`, `calendar`) for the progress text.
|
|
49
|
+
*/
|
|
50
|
+
readonly toolHighlights?: ReadonlyArray<string>;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Number of knowledge sources the agent will reference during the turn.
|
|
54
|
+
*/
|
|
55
|
+
readonly knowledgeSourceCount?: number;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Whether the calendar integration is wired up for this turn.
|
|
59
|
+
*/
|
|
60
|
+
readonly hasCalendarAccess?: boolean;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Whether the email-sending integration is wired up for this turn.
|
|
64
|
+
*/
|
|
65
|
+
readonly hasEmailAccess?: boolean;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Whether one or more project repositories are linked to this turn.
|
|
69
|
+
*/
|
|
70
|
+
readonly hasProjectAccess?: boolean;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Whether the AgentKit runtime for this agent came from the in-process cache.
|
|
74
|
+
*/
|
|
75
|
+
readonly isAgentKitCached?: boolean;
|
|
76
|
+
};
|
|
77
|
+
|
|
13
78
|
/**
|
|
14
79
|
* Default title used by durable chat progress cards.
|
|
15
80
|
*/
|
|
@@ -26,39 +91,25 @@ const USER_CHAT_PROGRESS_ITEM_IDS = {
|
|
|
26
91
|
} as const;
|
|
27
92
|
|
|
28
93
|
/**
|
|
29
|
-
* Static
|
|
94
|
+
* Static checklist mapping for one durable chat phase.
|
|
30
95
|
*/
|
|
31
|
-
type
|
|
32
|
-
readonly now: string;
|
|
33
|
-
readonly next: string;
|
|
96
|
+
type UserChatProgressPhaseChecklist = {
|
|
34
97
|
readonly completedItemIds: ReadonlySet<string>;
|
|
35
98
|
};
|
|
36
99
|
|
|
37
100
|
/**
|
|
38
|
-
*
|
|
101
|
+
* Static checklist progression shared across all durable chat phases.
|
|
39
102
|
*/
|
|
40
|
-
const
|
|
41
|
-
queued: {
|
|
42
|
-
|
|
43
|
-
next: 'The agent will read the conversation and prepare the answer.',
|
|
44
|
-
completedItemIds: new Set([]),
|
|
45
|
-
},
|
|
46
|
-
reading_context: {
|
|
47
|
-
now: 'Reading your message and the relevant conversation context.',
|
|
48
|
-
next: 'Prepare the agent instructions and working context.',
|
|
49
|
-
completedItemIds: new Set([USER_CHAT_PROGRESS_ITEM_IDS.RECEIVE_MESSAGE]),
|
|
50
|
-
},
|
|
103
|
+
const USER_CHAT_PROGRESS_PHASE_CHECKLISTS: Record<UserChatProgressPhase, UserChatProgressPhaseChecklist> = {
|
|
104
|
+
queued: { completedItemIds: new Set([]) },
|
|
105
|
+
reading_context: { completedItemIds: new Set([USER_CHAT_PROGRESS_ITEM_IDS.RECEIVE_MESSAGE]) },
|
|
51
106
|
preparing_agent: {
|
|
52
|
-
now: 'Preparing the agent instructions and context needed for this request.',
|
|
53
|
-
next: 'Check which tools and inputs are available.',
|
|
54
107
|
completedItemIds: new Set([
|
|
55
108
|
USER_CHAT_PROGRESS_ITEM_IDS.RECEIVE_MESSAGE,
|
|
56
109
|
USER_CHAT_PROGRESS_ITEM_IDS.PREPARE_AGENT,
|
|
57
110
|
]),
|
|
58
111
|
},
|
|
59
112
|
checking_capabilities: {
|
|
60
|
-
now: 'Checking available tools, attachments, and context before answering.',
|
|
61
|
-
next: 'Start working through the response.',
|
|
62
113
|
completedItemIds: new Set([
|
|
63
114
|
USER_CHAT_PROGRESS_ITEM_IDS.RECEIVE_MESSAGE,
|
|
64
115
|
USER_CHAT_PROGRESS_ITEM_IDS.PREPARE_AGENT,
|
|
@@ -66,8 +117,6 @@ const USER_CHAT_PROGRESS_PHASE_PRESENTATIONS: Record<UserChatProgressPhase, User
|
|
|
66
117
|
]),
|
|
67
118
|
},
|
|
68
119
|
starting_response: {
|
|
69
|
-
now: 'Working through the response. Progress updates will stay focused on visible actions and results.',
|
|
70
|
-
next: 'Stream the final answer as soon as it is ready.',
|
|
71
120
|
completedItemIds: new Set([
|
|
72
121
|
USER_CHAT_PROGRESS_ITEM_IDS.RECEIVE_MESSAGE,
|
|
73
122
|
USER_CHAT_PROGRESS_ITEM_IDS.PREPARE_AGENT,
|
|
@@ -103,19 +152,215 @@ const USER_CHAT_PROGRESS_ITEMS: ReadonlyArray<{
|
|
|
103
152
|
|
|
104
153
|
/**
|
|
105
154
|
* Creates one user-facing durable chat progress card for the current job phase.
|
|
155
|
+
*
|
|
156
|
+
* The optional `context` carries already-resolved runtime details (agent name, provider,
|
|
157
|
+
* tool count, attachment count, integrations) so the rendered `now`/`next` text reflects
|
|
158
|
+
* what the worker is actually doing on this turn instead of a generic scripted summary.
|
|
106
159
|
*/
|
|
107
|
-
export function createUserChatProgressCard(
|
|
108
|
-
|
|
160
|
+
export function createUserChatProgressCard(
|
|
161
|
+
phase: UserChatProgressPhase,
|
|
162
|
+
context?: UserChatProgressContext,
|
|
163
|
+
): NonNullable<ChatMessage['progressCard']> {
|
|
164
|
+
const checklist = USER_CHAT_PROGRESS_PHASE_CHECKLISTS[phase];
|
|
165
|
+
const { now, next } = composeUserChatProgressPhaseText(phase, context);
|
|
109
166
|
|
|
110
167
|
return {
|
|
111
168
|
title: USER_CHAT_PROGRESS_TITLE,
|
|
112
|
-
now
|
|
113
|
-
next
|
|
169
|
+
now,
|
|
170
|
+
next,
|
|
114
171
|
items: USER_CHAT_PROGRESS_ITEMS.map((item) => ({
|
|
115
172
|
...item,
|
|
116
|
-
status:
|
|
173
|
+
status: checklist.completedItemIds.has(item.id) ? 'completed' : 'pending',
|
|
117
174
|
})),
|
|
118
175
|
updatedAt: new Date().toISOString() as NonNullable<ChatMessage['progressCard']>['updatedAt'],
|
|
119
176
|
isVisible: true,
|
|
120
177
|
};
|
|
121
178
|
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Composes the dynamic `now`/`next` markdown shown for one durable chat phase.
|
|
182
|
+
*
|
|
183
|
+
* @private internal helper of `createUserChatProgressCard`
|
|
184
|
+
*/
|
|
185
|
+
function composeUserChatProgressPhaseText(
|
|
186
|
+
phase: UserChatProgressPhase,
|
|
187
|
+
context: UserChatProgressContext | undefined,
|
|
188
|
+
): { now: string; next: string } {
|
|
189
|
+
const agentLabel = describeAgent(context);
|
|
190
|
+
|
|
191
|
+
switch (phase) {
|
|
192
|
+
case 'queued':
|
|
193
|
+
return {
|
|
194
|
+
now: spaceTrim(`
|
|
195
|
+
Your message is waiting in the queue for the next available worker to pick it up.
|
|
196
|
+
`),
|
|
197
|
+
next: spaceTrim(`
|
|
198
|
+
Read the conversation and load ${agentLabel}.
|
|
199
|
+
`),
|
|
200
|
+
};
|
|
201
|
+
case 'reading_context': {
|
|
202
|
+
const attachmentHint = describeAttachmentHint(context);
|
|
203
|
+
return {
|
|
204
|
+
now: spaceTrim(`
|
|
205
|
+
Reading your latest message${attachmentHint} together with the earlier conversation thread.
|
|
206
|
+
`),
|
|
207
|
+
next: spaceTrim(`
|
|
208
|
+
Resolve ${agentLabel} and load its instructions, persona, and rules.
|
|
209
|
+
`),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
case 'preparing_agent': {
|
|
213
|
+
const knowledgeHint = describeKnowledgeHint(context);
|
|
214
|
+
return {
|
|
215
|
+
now: spaceTrim(`
|
|
216
|
+
Loading the instructions and configured commitments for ${agentLabel}${knowledgeHint}.
|
|
217
|
+
`),
|
|
218
|
+
next: spaceTrim(`
|
|
219
|
+
Check which tools, integrations, and attachments are usable for this turn.
|
|
220
|
+
`),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
case 'checking_capabilities': {
|
|
224
|
+
const capabilitiesText = describeCapabilities(context);
|
|
225
|
+
return {
|
|
226
|
+
now: spaceTrim(`
|
|
227
|
+
Verifying that ${agentLabel} can use ${capabilitiesText} for this answer.
|
|
228
|
+
`),
|
|
229
|
+
next: spaceTrim(`
|
|
230
|
+
Connect to the language model and start streaming the response.
|
|
231
|
+
`),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
case 'starting_response': {
|
|
235
|
+
const providerHint = describeProviderHint(context);
|
|
236
|
+
const cacheHint = context?.isAgentKitCached === false
|
|
237
|
+
? ' Fresh runtime is being prepared because no cached agent was available.'
|
|
238
|
+
: '';
|
|
239
|
+
return {
|
|
240
|
+
now: spaceTrim(`
|
|
241
|
+
Calling the language model${providerHint} and waiting for the first tokens.${cacheHint}
|
|
242
|
+
`),
|
|
243
|
+
next: spaceTrim(`
|
|
244
|
+
Stream the response and update this panel with each user-relevant decision, action, or result.
|
|
245
|
+
`),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Returns the user-facing label for the active agent based on available context.
|
|
253
|
+
*
|
|
254
|
+
* @private internal helper of `composeUserChatProgressPhaseText`
|
|
255
|
+
*/
|
|
256
|
+
function describeAgent(context: UserChatProgressContext | undefined): string {
|
|
257
|
+
if (context?.agentName) {
|
|
258
|
+
return `agent **${context.agentName}**`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (context?.agentPermanentId) {
|
|
262
|
+
return `agent \`${context.agentPermanentId}\``;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return 'the agent';
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Returns a fragment describing attachment count when one or more attachments are present.
|
|
270
|
+
*
|
|
271
|
+
* @private internal helper of `composeUserChatProgressPhaseText`
|
|
272
|
+
*/
|
|
273
|
+
function describeAttachmentHint(context: UserChatProgressContext | undefined): string {
|
|
274
|
+
const count = context?.attachmentCount ?? 0;
|
|
275
|
+
if (count <= 0) {
|
|
276
|
+
return '';
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return count === 1 ? ' and 1 attachment' : ` and ${count} attachments`;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Returns a fragment describing knowledge sources when one or more are configured.
|
|
284
|
+
*
|
|
285
|
+
* @private internal helper of `composeUserChatProgressPhaseText`
|
|
286
|
+
*/
|
|
287
|
+
function describeKnowledgeHint(context: UserChatProgressContext | undefined): string {
|
|
288
|
+
const count = context?.knowledgeSourceCount ?? 0;
|
|
289
|
+
if (count <= 0) {
|
|
290
|
+
return '';
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return count === 1 ? ' (1 knowledge source)' : ` (${count} knowledge sources)`;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Returns a fragment describing the LLM provider when already known.
|
|
298
|
+
*
|
|
299
|
+
* @private internal helper of `composeUserChatProgressPhaseText`
|
|
300
|
+
*/
|
|
301
|
+
function describeProviderHint(context: UserChatProgressContext | undefined): string {
|
|
302
|
+
if (!context?.provider) {
|
|
303
|
+
return '';
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return ` via ${context.provider}`;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Builds the markdown fragment describing tools, knowledge, attachments, and integrations.
|
|
311
|
+
*
|
|
312
|
+
* @private internal helper of `composeUserChatProgressPhaseText`
|
|
313
|
+
*/
|
|
314
|
+
function describeCapabilities(context: UserChatProgressContext | undefined): string {
|
|
315
|
+
if (!context) {
|
|
316
|
+
return 'its configured tools and inputs';
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const fragments: Array<string> = [];
|
|
320
|
+
|
|
321
|
+
const toolCount = context.toolCount ?? 0;
|
|
322
|
+
if (toolCount > 0) {
|
|
323
|
+
const highlights = (context.toolHighlights ?? []).filter((entry) => entry.trim().length > 0);
|
|
324
|
+
if (highlights.length > 0) {
|
|
325
|
+
fragments.push(`${toolCount === 1 ? '1 tool' : `${toolCount} tools`} (${highlights.join(', ')})`);
|
|
326
|
+
} else {
|
|
327
|
+
fragments.push(toolCount === 1 ? '1 tool' : `${toolCount} tools`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const knowledgeCount = context.knowledgeSourceCount ?? 0;
|
|
332
|
+
if (knowledgeCount > 0) {
|
|
333
|
+
fragments.push(knowledgeCount === 1 ? '1 knowledge source' : `${knowledgeCount} knowledge sources`);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const attachmentCount = context.attachmentCount ?? 0;
|
|
337
|
+
if (attachmentCount > 0) {
|
|
338
|
+
fragments.push(attachmentCount === 1 ? '1 attachment' : `${attachmentCount} attachments`);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const integrations: Array<string> = [];
|
|
342
|
+
if (context.hasCalendarAccess) {
|
|
343
|
+
integrations.push('calendar');
|
|
344
|
+
}
|
|
345
|
+
if (context.hasEmailAccess) {
|
|
346
|
+
integrations.push('email');
|
|
347
|
+
}
|
|
348
|
+
if (context.hasProjectAccess) {
|
|
349
|
+
integrations.push('project repository');
|
|
350
|
+
}
|
|
351
|
+
if (integrations.length > 0) {
|
|
352
|
+
fragments.push(`${integrations.join(' / ')} access`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (fragments.length === 0) {
|
|
356
|
+
return 'its configured instructions and inputs';
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (fragments.length === 1) {
|
|
360
|
+
return fragments[0]!;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const head = fragments.slice(0, -1).join(', ');
|
|
364
|
+
const tail = fragments[fragments.length - 1]!;
|
|
365
|
+
return `${head}, and ${tail}`;
|
|
366
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NextRequest } from 'next/server';
|
|
2
2
|
import { $getTableName } from '../database/$getTableName';
|
|
3
3
|
import { $provideSupabaseForServer } from '../database/$provideSupabaseForServer';
|
|
4
|
+
import { parseSessionToken, SESSION_COOKIE_NAME } from './session';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Result of Api key validation.
|
|
@@ -21,10 +22,13 @@ export type ApiKeyValidationResult = {
|
|
|
21
22
|
export async function validateApiKey(request: NextRequest): Promise<ApiKeyValidationResult> {
|
|
22
23
|
const authHeader = request.headers.get('authorization');
|
|
23
24
|
|
|
24
|
-
// If no auth header, check if user has a session cookie (logged in via web)
|
|
25
|
+
// If no auth header, check if user has a valid signed session cookie (logged in via web).
|
|
26
|
+
// The cookie value must pass HMAC-signature verification — merely sending a `sessionToken`
|
|
27
|
+
// cookie with arbitrary content must NOT authenticate the request.
|
|
25
28
|
if (!authHeader) {
|
|
26
|
-
const
|
|
27
|
-
|
|
29
|
+
const sessionToken = request.cookies.get(SESSION_COOKIE_NAME)?.value;
|
|
30
|
+
const session = parseSessionToken(sessionToken);
|
|
31
|
+
if (session !== null) {
|
|
28
32
|
return { isValid: true };
|
|
29
33
|
}
|
|
30
34
|
return {
|
|
@@ -207,6 +207,10 @@ export type VpsSelfUpdateOverview = {
|
|
|
207
207
|
* Number of commits the deployed checkout is behind the latest remote commit, or `null` when unknown.
|
|
208
208
|
*/
|
|
209
209
|
readonly commitsBehindCount: number | null;
|
|
210
|
+
/**
|
|
211
|
+
* Commits that the deployed checkout is behind the latest remote commit (newest first).
|
|
212
|
+
*/
|
|
213
|
+
readonly pendingCommits: ReadonlyArray<VpsSelfUpdatePendingCommit>;
|
|
210
214
|
/**
|
|
211
215
|
* Whether the remote branch contains a newer commit than the deployed checkout.
|
|
212
216
|
*/
|
|
@@ -465,6 +469,10 @@ export async function readVpsSelfUpdateOverview(): Promise<VpsSelfUpdateOverview
|
|
|
465
469
|
currentCommitSha && latestRemoteCommitSha
|
|
466
470
|
? await countCommitsBetween(repositoryDirectory, currentCommitSha, latestRemoteCommitSha)
|
|
467
471
|
: null;
|
|
472
|
+
const pendingCommits =
|
|
473
|
+
currentCommitSha && latestRemoteCommitSha
|
|
474
|
+
? await listCommitsBetween(repositoryDirectory, currentCommitSha, latestRemoteCommitSha)
|
|
475
|
+
: [];
|
|
468
476
|
const resolvedJob = resolveVpsSelfUpdateJobForOverview(job, {
|
|
469
477
|
currentEnvironment,
|
|
470
478
|
currentCommitSha,
|
|
@@ -484,6 +492,7 @@ export async function readVpsSelfUpdateOverview(): Promise<VpsSelfUpdateOverview
|
|
|
484
492
|
latestRemoteCommitShortSha: abbreviateCommitSha(latestRemoteCommitSha),
|
|
485
493
|
latestRemoteCommitDate,
|
|
486
494
|
commitsBehindCount,
|
|
495
|
+
pendingCommits,
|
|
487
496
|
isUpdateAvailable: Boolean(
|
|
488
497
|
currentCommitSha && latestRemoteCommitSha && currentCommitSha !== latestRemoteCommitSha,
|
|
489
498
|
),
|
|
@@ -494,6 +503,33 @@ export async function readVpsSelfUpdateOverview(): Promise<VpsSelfUpdateOverview
|
|
|
494
503
|
};
|
|
495
504
|
}
|
|
496
505
|
|
|
506
|
+
/**
|
|
507
|
+
* Browser-safe summary of one commit that the deployed checkout is behind the latest remote commit.
|
|
508
|
+
*/
|
|
509
|
+
export type VpsSelfUpdatePendingCommit = {
|
|
510
|
+
/**
|
|
511
|
+
* Full commit hash.
|
|
512
|
+
*/
|
|
513
|
+
readonly commitSha: string;
|
|
514
|
+
/**
|
|
515
|
+
* Short commit hash (first 7 chars).
|
|
516
|
+
*/
|
|
517
|
+
readonly shortCommitSha: string;
|
|
518
|
+
/**
|
|
519
|
+
* Single-line commit subject.
|
|
520
|
+
*/
|
|
521
|
+
readonly subject: string;
|
|
522
|
+
/**
|
|
523
|
+
* Author timestamp in ISO format or `null` when unknown.
|
|
524
|
+
*/
|
|
525
|
+
readonly authoredAt: string | null;
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Hard ceiling for the pending-commits listing returned in the overview to avoid huge payloads on a long-stale server.
|
|
530
|
+
*/
|
|
531
|
+
const VPS_SELF_UPDATE_MAX_PENDING_COMMITS = 100;
|
|
532
|
+
|
|
497
533
|
/**
|
|
498
534
|
* Browser-safe summary of one commit that the super admin can pick from the custom-target picker.
|
|
499
535
|
*/
|
|
@@ -790,6 +826,7 @@ function createUnavailableOverview(context: {
|
|
|
790
826
|
latestRemoteCommitShortSha: null,
|
|
791
827
|
latestRemoteCommitDate: null,
|
|
792
828
|
commitsBehindCount: null,
|
|
829
|
+
pendingCommits: [],
|
|
793
830
|
isUpdateAvailable: false,
|
|
794
831
|
originRepositoryUrl: context.originRepositoryUrl,
|
|
795
832
|
isOriginRepositoryDefault: context.originRepositoryUrl === VPS_SELF_UPDATE_DEFAULT_ORIGIN_REPOSITORY_URL,
|
|
@@ -1205,6 +1242,79 @@ async function countCommitsBetween(
|
|
|
1205
1242
|
return Number.isFinite(parsedCount) ? parsedCount : null;
|
|
1206
1243
|
}
|
|
1207
1244
|
|
|
1245
|
+
/**
|
|
1246
|
+
* Lists commits that separate two commits in the local repository so the admin UI can show subject/hash/date for each one.
|
|
1247
|
+
*
|
|
1248
|
+
* Returns an empty list when either commit cannot be resolved (typical for a shallow clone that has not been deepened yet)
|
|
1249
|
+
* or when both commits are identical.
|
|
1250
|
+
*
|
|
1251
|
+
* @param repositoryDirectory - Repository checkout path.
|
|
1252
|
+
* @param fromCommitSha - Older commit hash (deployed commit).
|
|
1253
|
+
* @param toCommitSha - Newer commit hash (latest remote commit).
|
|
1254
|
+
* @returns Browser-safe pending-commit list (newest first).
|
|
1255
|
+
*/
|
|
1256
|
+
async function listCommitsBetween(
|
|
1257
|
+
repositoryDirectory: string,
|
|
1258
|
+
fromCommitSha: string,
|
|
1259
|
+
toCommitSha: string,
|
|
1260
|
+
): Promise<ReadonlyArray<VpsSelfUpdatePendingCommit>> {
|
|
1261
|
+
if (fromCommitSha === toCommitSha) {
|
|
1262
|
+
return [];
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
const output = await runGitInRepository(repositoryDirectory, [
|
|
1266
|
+
'log',
|
|
1267
|
+
`--max-count=${VPS_SELF_UPDATE_MAX_PENDING_COMMITS}`,
|
|
1268
|
+
`--format=%H${GIT_LOG_FIELD_SEPARATOR}%aI${GIT_LOG_FIELD_SEPARATOR}%s`,
|
|
1269
|
+
`${fromCommitSha}..${toCommitSha}`,
|
|
1270
|
+
]);
|
|
1271
|
+
if (!output) {
|
|
1272
|
+
return [];
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
const pendingCommits: Array<VpsSelfUpdatePendingCommit> = [];
|
|
1276
|
+
for (const line of output.split('\n')) {
|
|
1277
|
+
if (!line) {
|
|
1278
|
+
continue;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
const fields = line.split(GIT_LOG_FIELD_SEPARATOR);
|
|
1282
|
+
const commitSha = fields[0] ?? '';
|
|
1283
|
+
if (!commitSha) {
|
|
1284
|
+
continue;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
const authoredAt = fields[1] || null;
|
|
1288
|
+
const subject = fields.slice(2).join(GIT_LOG_FIELD_SEPARATOR);
|
|
1289
|
+
|
|
1290
|
+
pendingCommits.push({
|
|
1291
|
+
commitSha,
|
|
1292
|
+
shortCommitSha: commitSha.slice(0, 7),
|
|
1293
|
+
subject,
|
|
1294
|
+
authoredAt,
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
return pendingCommits;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
/**
|
|
1302
|
+
* Reads the full persisted standalone VPS self-update log so the super admin can copy/download it for debugging.
|
|
1303
|
+
*
|
|
1304
|
+
* @returns Log file content or `null` when the file does not exist yet.
|
|
1305
|
+
*/
|
|
1306
|
+
export async function readVpsSelfUpdateLogFileContent(): Promise<string | null> {
|
|
1307
|
+
const logFilePath = resolveVpsSelfUpdateLogFilePath();
|
|
1308
|
+
try {
|
|
1309
|
+
return await readFile(logFilePath, 'utf-8');
|
|
1310
|
+
} catch (error) {
|
|
1311
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
1312
|
+
return null;
|
|
1313
|
+
}
|
|
1314
|
+
throw error;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1208
1318
|
/**
|
|
1209
1319
|
* Checks whether a detached update process is still alive.
|
|
1210
1320
|
*
|