@promptbook/cli 0.112.0-125 → 0.112.0-127

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 (190) hide show
  1. package/README.md +5 -5
  2. package/apps/agents-server/README.md +3 -0
  3. package/apps/agents-server/playwright.config.ts +1 -0
  4. package/apps/agents-server/public/promptbook-logo-blue.png +0 -0
  5. package/apps/agents-server/public/promptbook-logo-white.png +0 -0
  6. package/apps/agents-server/src/app/[agentName]/layout.tsx +1 -0
  7. package/apps/agents-server/src/app/actions.ts +2 -2
  8. package/apps/agents-server/src/app/admin/update/UpdateClient.tsx +68 -36
  9. package/apps/agents-server/src/app/agents/[agentName]/ActiveAgentBreadcrumbBinder.tsx +30 -0
  10. package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +2 -0
  11. package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatHistoryPayloadState.ts +38 -4
  12. package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatHistorySyncOperations.ts +4 -1
  13. package/apps/agents-server/src/app/agents/[agentName]/chat/CanonicalAgentChatSurface.tsx +19 -15
  14. package/apps/agents-server/src/app/agents/[agentName]/chat/useCanonicalAgentChatPanelState.ts +58 -7
  15. package/apps/agents-server/src/app/agents/[agentName]/layout.tsx +47 -1
  16. package/apps/agents-server/src/app/api/chat/citation-label/route.ts +133 -0
  17. package/apps/agents-server/src/app/globals.css +78 -0
  18. package/apps/agents-server/src/components/Header/ActiveAgentBreadcrumbContext.tsx +82 -0
  19. package/apps/agents-server/src/components/Header/useHeaderActiveAgent.ts +26 -7
  20. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +35 -32
  21. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/ManGoNewAgentWizard.tsx +151 -0
  22. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/ManGoOnboardingNavigation.tsx +50 -0
  23. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/BookLanguagePanel.tsx +125 -0
  24. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/DropZone.tsx +83 -0
  25. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/EmailTestRun.tsx +216 -0
  26. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/KnowledgeList.tsx +79 -0
  27. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/Logo.tsx +20 -0
  28. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/MarkdownBookEditor.tsx +154 -0
  29. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/MarkdownPreview.tsx +188 -0
  30. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/RailStepper.tsx +115 -0
  31. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/StepFrame.tsx +47 -0
  32. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/TestChat.tsx +159 -0
  33. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/WizardShell.tsx +114 -0
  34. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/brand/RailArtwork.tsx +60 -0
  35. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/BookStep.tsx +135 -0
  36. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/DoneStep.tsx +215 -0
  37. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/KnowledgeStep.tsx +152 -0
  38. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/TestStep.tsx +262 -0
  39. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/ZadaniStep.tsx +64 -0
  40. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Badge.tsx +41 -0
  41. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Banner.tsx +69 -0
  42. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Button.tsx +77 -0
  43. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Card.tsx +33 -0
  44. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Field.tsx +103 -0
  45. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/IconButton.tsx +40 -0
  46. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Spinner.tsx +14 -0
  47. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/tokens.ts +37 -0
  48. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/config/bookSections.ts +62 -0
  49. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/config/emailScenarios.ts +22 -0
  50. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/config/steps.ts +46 -0
  51. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/index.ts +11 -0
  52. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/lib/cn.ts +4 -0
  53. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/lib/format.ts +9 -0
  54. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/lib/id.ts +11 -0
  55. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/agentEvalService.ts +30 -0
  56. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/agentTestService.ts +69 -0
  57. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/bookService.ts +21 -0
  58. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/createManGoAgentSource.ts +107 -0
  59. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/draftService.ts +45 -0
  60. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/uploadService.ts +27 -0
  61. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/state/OnboardingProvider.tsx +121 -0
  62. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/types.ts +52 -0
  63. package/apps/agents-server/src/components/NewAgentDialog/createNewAgentWizardSource.ts +1 -1
  64. package/apps/agents-server/src/components/NewAgentDialog/trackNewAgentCreationEvent.ts +1 -1
  65. package/apps/agents-server/src/components/NewAgentDialog/useNewAgentDialog.tsx +76 -0
  66. package/apps/agents-server/src/constants/newAgentWizard.ts +17 -8
  67. package/apps/agents-server/src/database/getMetadata.ts +37 -1
  68. package/apps/agents-server/src/database/metadataDefaults.ts +18 -7
  69. package/apps/agents-server/src/database/migrations/2026-06-2200-new-agent-wizard-metadata-key.sql +27 -0
  70. package/apps/agents-server/src/database/migrations/2026-06-2201-new-agent-wizard-mango-default.sql +11 -0
  71. package/apps/agents-server/src/generated/reservedPaths.ts +2 -0
  72. package/apps/agents-server/src/languages/translations/english.yaml +1 -1
  73. package/apps/agents-server/src/tools/createChatAttachmentToolFunctions.ts +8 -4
  74. package/apps/agents-server/src/utils/chat/resolveAgentsServerCitationLabel.ts +130 -0
  75. package/apps/agents-server/src/utils/chat/resolveCitationSourceLabel.ts +436 -0
  76. package/apps/agents-server/src/utils/knowledge/resolveWebsiteKnowledgeSourcesForServer.ts +10 -1
  77. package/apps/agents-server/src/utils/session.ts +62 -5
  78. package/apps/agents-server/src/utils/transpilers/resolveTranspiledTeamExport.ts +10 -10
  79. package/apps/agents-server/src/utils/userChat/userChatProgressCard.ts +198 -63
  80. package/apps/agents-server/src/utils/vpsConfiguration.ts +1 -0
  81. package/apps/agents-server/src/utils/vpsSelfUpdate.ts +81 -8
  82. package/esm/index.es.js +1052 -455
  83. package/esm/index.es.js.map +1 -1
  84. package/esm/scripts/run-codex-prompts/git/commitChanges.d.ts +3 -1
  85. package/esm/scripts/run-codex-prompts/main/runPromptRound.d.ts +2 -2
  86. package/esm/scripts/run-codex-prompts/server/buildCoderServerPromptResponse.d.ts +50 -0
  87. package/esm/scripts/run-codex-prompts/server/buildCoderServerRunState.d.ts +23 -0
  88. package/esm/scripts/run-codex-prompts/server/coderServerHtml.d.ts +1 -1
  89. package/esm/scripts/run-codex-prompts/server/runCoderHttpServer.d.ts +13 -1
  90. package/esm/scripts/run-codex-prompts/server/updatePromptSection.d.ts +1 -1
  91. package/esm/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +1 -0
  92. package/esm/scripts/run-codex-prompts/ui/buildRunUiFrameShared.d.ts +4 -0
  93. package/esm/scripts/run-codex-prompts/ui/renderCoderRunUi.d.ts +1 -0
  94. package/esm/src/_packages/components.index.d.ts +2 -0
  95. package/esm/src/_packages/types.index.d.ts +2 -0
  96. package/esm/src/avatars/avatarAnimationScheduler.d.ts +4 -0
  97. package/esm/src/book-components/Chat/Chat/ChatCitationModal.d.ts +2 -0
  98. package/esm/src/book-components/Chat/Chat/ChatMessageItem.d.ts +4 -0
  99. package/esm/src/book-components/Chat/Chat/ChatMessageList.d.ts +4 -0
  100. package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
  101. package/esm/src/book-components/Chat/Chat/ChatToolCallModal.d.ts +2 -0
  102. package/esm/src/book-components/Chat/Chat/ChatToolCallModalContent.d.ts +3 -1
  103. package/esm/src/book-components/Chat/Chat/TeamToolCallModalContent.d.ts +2 -0
  104. package/esm/src/book-components/Chat/SourceChip/SourceChip.d.ts +6 -1
  105. package/esm/src/book-components/Chat/hooks/useResolvedCitationLabel.d.ts +12 -0
  106. package/esm/src/book-components/Chat/types/ChatMessage.d.ts +4 -0
  107. package/esm/src/book-components/Chat/types/ChatParticipant.d.ts +1 -1
  108. package/esm/src/book-components/Chat/types/CitationLabelResolver.d.ts +8 -0
  109. package/esm/src/book-components/Chat/utils/citationHelpers.d.ts +9 -0
  110. package/esm/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.d.ts +14 -0
  111. package/esm/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.test.d.ts +1 -0
  112. package/esm/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +4 -0
  113. package/esm/src/version.d.ts +1 -1
  114. package/package.json +1 -1
  115. package/src/_packages/components.index.ts +2 -0
  116. package/src/_packages/types.index.ts +2 -0
  117. package/src/avatars/avatarAnimationScheduler.ts +61 -20
  118. package/src/avatars/visuals/octopus3d2AvatarVisual.ts +106 -21
  119. package/src/avatars/visuals/octopus3d3AvatarVisual.ts +131 -28
  120. package/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/augmentAgentModelRequirementsFromSource.ts +18 -2
  121. package/src/book-2.0/book-language-documentation/renderGroupedCommitmentDocumentationMarkdown.ts +7 -1
  122. package/src/book-components/Chat/Chat/Chat.tsx +4 -0
  123. package/src/book-components/Chat/Chat/ChatCitationModal.tsx +17 -3
  124. package/src/book-components/Chat/Chat/ChatMessageItem.tsx +22 -2
  125. package/src/book-components/Chat/Chat/ChatMessageList.tsx +6 -0
  126. package/src/book-components/Chat/Chat/ChatProps.tsx +6 -0
  127. package/src/book-components/Chat/Chat/ChatToolCallModal.tsx +4 -0
  128. package/src/book-components/Chat/Chat/ChatToolCallModalContent.tsx +4 -0
  129. package/src/book-components/Chat/Chat/TeamToolCallModalContent.tsx +16 -3
  130. package/src/book-components/Chat/Chat/useChatPostprocessedMessages.ts +13 -2
  131. package/src/book-components/Chat/SourceChip/SourceChip.module.css +8 -0
  132. package/src/book-components/Chat/SourceChip/SourceChip.tsx +19 -5
  133. package/src/book-components/Chat/hooks/useResolvedCitationLabel.ts +65 -0
  134. package/src/book-components/Chat/save/html/htmlSaveFormatDefinition.ts +315 -88
  135. package/src/book-components/Chat/save/react/reactSaveFormatDefinition.ts +7 -1
  136. package/src/book-components/Chat/types/ChatMessage.ts +5 -0
  137. package/src/book-components/Chat/types/ChatParticipant.ts +1 -1
  138. package/src/book-components/Chat/types/CitationLabelResolver.ts +9 -0
  139. package/src/book-components/Chat/utils/citationHelpers.ts +90 -2
  140. package/src/book-components/Chat/utils/createCitationFootnoteRenderModel.ts +3 -0
  141. package/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.ts +178 -0
  142. package/src/book-components/Chat/utils/parseCitationsFromContent.ts +6 -0
  143. package/src/cli/cli-commands/agents-server/ensureAgentsServerEnvFile.ts +13 -2
  144. package/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts +1 -1
  145. package/src/cli/cli-commands/coder/run.ts +17 -20
  146. package/src/cli/cli-commands/coder/server.ts +13 -16
  147. package/src/cli/common/$deprecateCliCommand.ts +10 -1
  148. package/src/commitments/GOAL/GOAL.ts +7 -2
  149. package/src/commitments/LANGUAGE/LANGUAGE.ts +7 -2
  150. package/src/commitments/TEAM/TEAM.ts +12 -1
  151. package/src/commitments/USE_PROJECT/createUseProjectToolFunctions.ts +8 -1
  152. package/src/import-plugins/JsonFileImportPlugin.ts +15 -2
  153. package/src/import-plugins/TextFileImportPlugin.ts +8 -1
  154. package/src/llm-providers/_common/register/$registeredLlmToolsMessage.ts +6 -1
  155. package/src/other/templates/getTemplatesPipelineCollection.ts +696 -941
  156. package/src/utils/chat/chatAttachments/resolveChatAttachmentContent.ts +8 -1
  157. package/src/version.ts +2 -2
  158. package/src/versions.txt +2 -1
  159. package/umd/index.umd.js +1055 -458
  160. package/umd/index.umd.js.map +1 -1
  161. package/umd/scripts/run-codex-prompts/git/commitChanges.d.ts +3 -1
  162. package/umd/scripts/run-codex-prompts/main/runPromptRound.d.ts +2 -2
  163. package/umd/scripts/run-codex-prompts/server/buildCoderServerPromptResponse.d.ts +50 -0
  164. package/umd/scripts/run-codex-prompts/server/buildCoderServerRunState.d.ts +23 -0
  165. package/umd/scripts/run-codex-prompts/server/coderServerHtml.d.ts +1 -1
  166. package/umd/scripts/run-codex-prompts/server/runCoderHttpServer.d.ts +13 -1
  167. package/umd/scripts/run-codex-prompts/server/updatePromptSection.d.ts +1 -1
  168. package/umd/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +1 -0
  169. package/umd/scripts/run-codex-prompts/ui/buildRunUiFrameShared.d.ts +4 -0
  170. package/umd/scripts/run-codex-prompts/ui/renderCoderRunUi.d.ts +1 -0
  171. package/umd/src/_packages/components.index.d.ts +2 -0
  172. package/umd/src/_packages/types.index.d.ts +2 -0
  173. package/umd/src/avatars/avatarAnimationScheduler.d.ts +4 -0
  174. package/umd/src/book-components/Chat/Chat/ChatCitationModal.d.ts +2 -0
  175. package/umd/src/book-components/Chat/Chat/ChatMessageItem.d.ts +4 -0
  176. package/umd/src/book-components/Chat/Chat/ChatMessageList.d.ts +4 -0
  177. package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
  178. package/umd/src/book-components/Chat/Chat/ChatToolCallModal.d.ts +2 -0
  179. package/umd/src/book-components/Chat/Chat/ChatToolCallModalContent.d.ts +3 -1
  180. package/umd/src/book-components/Chat/Chat/TeamToolCallModalContent.d.ts +2 -0
  181. package/umd/src/book-components/Chat/SourceChip/SourceChip.d.ts +6 -1
  182. package/umd/src/book-components/Chat/hooks/useResolvedCitationLabel.d.ts +12 -0
  183. package/umd/src/book-components/Chat/types/ChatMessage.d.ts +4 -0
  184. package/umd/src/book-components/Chat/types/ChatParticipant.d.ts +1 -1
  185. package/umd/src/book-components/Chat/types/CitationLabelResolver.d.ts +8 -0
  186. package/umd/src/book-components/Chat/utils/citationHelpers.d.ts +9 -0
  187. package/umd/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.d.ts +14 -0
  188. package/umd/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.test.d.ts +1 -0
  189. package/umd/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +4 -0
  190. package/umd/src/version.d.ts +1 -1
@@ -0,0 +1,130 @@
1
+ 'use client';
2
+
3
+ import type { ChatProps } from '@promptbook-local/components';
4
+
5
+ /**
6
+ * Citation label resolver type accepted by the shared Chat component.
7
+ *
8
+ * @private utility type of Agents Server chat citations
9
+ */
10
+ type AgentsServerCitationLabelResolver = NonNullable<ChatProps['resolveCitationLabel']>;
11
+
12
+ /**
13
+ * Cache of in-flight and completed citation label lookups.
14
+ *
15
+ * @private utility constant of Agents Server chat citations
16
+ */
17
+ const citationLabelCache = new Map<string, Promise<string | null>>();
18
+
19
+ /**
20
+ * Pattern matching HTTP(S) URLs that the server can fetch.
21
+ *
22
+ * @private utility constant of Agents Server chat citations
23
+ */
24
+ const HTTP_URL_REGEX = /^https?:\/\//i;
25
+
26
+ /**
27
+ * Resolves nicer citation labels for Agents Server chat sources.
28
+ *
29
+ * @private utility of Agents Server chat citations
30
+ */
31
+ export const resolveAgentsServerCitationLabel: AgentsServerCitationLabelResolver = async (citation) => {
32
+ const title = normalizeCitationLabelResponse(citation.title);
33
+ if (title) {
34
+ return title;
35
+ }
36
+
37
+ if (!hasResolvableCitationTarget(citation.source, citation.url)) {
38
+ return null;
39
+ }
40
+
41
+ const cacheKey = createCitationLabelCacheKey(citation.source, citation.url, citation.title);
42
+ const cachedLabel = citationLabelCache.get(cacheKey);
43
+ if (cachedLabel) {
44
+ return cachedLabel;
45
+ }
46
+
47
+ const labelPromise = requestCitationLabel({
48
+ source: citation.source,
49
+ url: citation.url,
50
+ title: citation.title,
51
+ });
52
+ citationLabelCache.set(cacheKey, labelPromise);
53
+
54
+ return labelPromise;
55
+ };
56
+
57
+ /**
58
+ * Requests one citation label from the server.
59
+ *
60
+ * @param payload - Citation metadata sent to the server resolver.
61
+ * @returns Resolved label or null.
62
+ *
63
+ * @private utility of Agents Server chat citations
64
+ */
65
+ async function requestCitationLabel(payload: {
66
+ readonly source: string;
67
+ readonly url?: string;
68
+ readonly title?: string;
69
+ }): Promise<string | null> {
70
+ const response = await fetch('/api/chat/citation-label', {
71
+ method: 'POST',
72
+ headers: {
73
+ 'Content-Type': 'application/json',
74
+ },
75
+ body: JSON.stringify(payload),
76
+ }).catch(() => null);
77
+
78
+ if (!response?.ok) {
79
+ return null;
80
+ }
81
+
82
+ const body = (await response.json().catch(() => null)) as { label?: unknown } | null;
83
+
84
+ return normalizeCitationLabelResponse(body?.label);
85
+ }
86
+
87
+ /**
88
+ * Creates a stable cache key for one citation lookup.
89
+ *
90
+ * @param source - Citation source.
91
+ * @param url - Optional resolved citation URL.
92
+ * @param title - Optional title.
93
+ * @returns Cache key.
94
+ *
95
+ * @private utility of Agents Server chat citations
96
+ */
97
+ function createCitationLabelCacheKey(source: string, url?: string, title?: string): string {
98
+ return [source, url || '', title || ''].join('\n');
99
+ }
100
+
101
+ /**
102
+ * Checks whether a citation has a URL target worth resolving on the server.
103
+ *
104
+ * @param source - Citation source.
105
+ * @param url - Optional resolved citation URL.
106
+ * @returns True when the server can attempt a metadata lookup.
107
+ *
108
+ * @private utility of Agents Server chat citations
109
+ */
110
+ function hasResolvableCitationTarget(source: string, url?: string): boolean {
111
+ return HTTP_URL_REGEX.test(url || '') || HTTP_URL_REGEX.test(source);
112
+ }
113
+
114
+ /**
115
+ * Normalizes one label value received from the server or structured citation metadata.
116
+ *
117
+ * @param value - Raw label value.
118
+ * @returns Trimmed label or null.
119
+ *
120
+ * @private utility of Agents Server chat citations
121
+ */
122
+ function normalizeCitationLabelResponse(value: unknown): string | null {
123
+ if (typeof value !== 'string') {
124
+ return null;
125
+ }
126
+
127
+ const label = value.replace(/\s+/g, ' ').trim();
128
+
129
+ return label || null;
130
+ }
@@ -0,0 +1,436 @@
1
+ import { JSDOM } from 'jsdom';
2
+
3
+ /**
4
+ * Citation metadata accepted by the Agents Server source-label resolver.
5
+ *
6
+ * @private utility type of Agents Server chat citation labels
7
+ */
8
+ export type CitationSourceLabelPayload = {
9
+ /**
10
+ * Raw citation source from the model response.
11
+ */
12
+ readonly source: string;
13
+
14
+ /**
15
+ * Optional resolved URL for the citation source.
16
+ */
17
+ readonly url?: string;
18
+
19
+ /**
20
+ * Optional title already attached to the citation.
21
+ */
22
+ readonly title?: string;
23
+ };
24
+
25
+ /**
26
+ * Maximum number of bytes fetched when resolving one citation title.
27
+ *
28
+ * @private utility constant of Agents Server chat citation labels
29
+ */
30
+ const MAX_SOURCE_LABEL_BYTES = 1_000_000;
31
+
32
+ /**
33
+ * Timeout for metadata fetches so the chat UI is not blocked by slow sources.
34
+ *
35
+ * @private utility constant of Agents Server chat citation labels
36
+ */
37
+ const SOURCE_LABEL_FETCH_TIMEOUT_MS = 4_000;
38
+
39
+ /**
40
+ * User agent used for lightweight source metadata requests.
41
+ *
42
+ * @private utility constant of Agents Server chat citation labels
43
+ */
44
+ const SOURCE_LABEL_USER_AGENT = 'Promptbook Agents Server citation label resolver';
45
+
46
+ /**
47
+ * Pattern matching markdown headings.
48
+ *
49
+ * @private utility constant of Agents Server chat citation labels
50
+ */
51
+ const MARKDOWN_HEADING_REGEX = /^\s{0,3}#{1,6}\s+(.+?)\s*#*\s*$/m;
52
+
53
+ /**
54
+ * Pattern matching common PDF/XMP title metadata.
55
+ *
56
+ * @private utility constant of Agents Server chat citation labels
57
+ */
58
+ const PDF_XMP_TITLE_REGEX = /<dc:title\b[^>]*>[\s\S]*?<rdf:li\b[^>]*>([\s\S]*?)<\/rdf:li>[\s\S]*?<\/dc:title>/i;
59
+
60
+ /**
61
+ * Pattern matching simple PDF/XMP title metadata without `rdf:li`.
62
+ *
63
+ * @private utility constant of Agents Server chat citation labels
64
+ */
65
+ const PDF_XMP_SIMPLE_TITLE_REGEX = /<dc:title\b[^>]*>([\s\S]*?)<\/dc:title>/i;
66
+
67
+ /**
68
+ * Pattern matching PDF literal title metadata.
69
+ *
70
+ * @private utility constant of Agents Server chat citation labels
71
+ */
72
+ const PDF_LITERAL_TITLE_REGEX = /\/Title\s*\(((?:\\.|[^\\)]){1,500})\)/s;
73
+
74
+ /**
75
+ * Pattern matching PDF hexadecimal title metadata.
76
+ *
77
+ * @private utility constant of Agents Server chat citation labels
78
+ */
79
+ const PDF_HEX_TITLE_REGEX = /\/Title\s*<([0-9a-fA-F\s]{2,1000})>/;
80
+
81
+ /**
82
+ * Returns the HTTP(S) URL that can be fetched for richer citation metadata.
83
+ *
84
+ * @param citation - Citation payload received from the chat UI.
85
+ * @returns URL string or null when the citation has no fetchable target.
86
+ *
87
+ * @private utility of Agents Server chat citation labels
88
+ */
89
+ export function resolveCitationLabelTargetUrl(citation: CitationSourceLabelPayload): string | null {
90
+ return normalizeHttpUrl(citation.url) || normalizeHttpUrl(citation.source);
91
+ }
92
+
93
+ /**
94
+ * Resolves a human-readable source label from page or document metadata.
95
+ *
96
+ * @param citation - Citation metadata from the chat UI.
97
+ * @returns Resolved label or null when no useful title metadata is available.
98
+ *
99
+ * @private utility of Agents Server chat citation labels
100
+ */
101
+ export async function resolveCitationSourceLabel(citation: CitationSourceLabelPayload): Promise<string | null> {
102
+ const explicitTitle = normalizeResolvedCitationLabel(citation.title);
103
+ if (explicitTitle) {
104
+ return explicitTitle;
105
+ }
106
+
107
+ const targetUrl = resolveCitationLabelTargetUrl(citation);
108
+ if (!targetUrl) {
109
+ return null;
110
+ }
111
+
112
+ const response = await fetch(targetUrl, {
113
+ headers: {
114
+ 'User-Agent': SOURCE_LABEL_USER_AGENT,
115
+ Accept: 'text/html,application/xhtml+xml,application/pdf,text/markdown,text/plain;q=0.9,*/*;q=0.1',
116
+ },
117
+ signal: AbortSignal.timeout(SOURCE_LABEL_FETCH_TIMEOUT_MS),
118
+ });
119
+
120
+ if (!response.ok) {
121
+ return null;
122
+ }
123
+
124
+ const bytes = await readResponseSnippet(response, MAX_SOURCE_LABEL_BYTES);
125
+ const contentType = response.headers.get('content-type')?.toLowerCase() || '';
126
+ const isPdf = contentType.includes('application/pdf') || targetUrl.toLowerCase().split('?')[0]?.endsWith('.pdf');
127
+
128
+ if (isPdf || startsWithPdfSignature(bytes)) {
129
+ return extractCitationLabelFromPdfBytes(bytes);
130
+ }
131
+
132
+ const text = new TextDecoder('utf-8', { fatal: false }).decode(bytes);
133
+ if (contentType.includes('markdown') || contentType.includes('text/plain')) {
134
+ return extractCitationLabelFromPlainText(text);
135
+ }
136
+
137
+ return extractCitationLabelFromHtml(text) || extractCitationLabelFromPlainText(text);
138
+ }
139
+
140
+ /**
141
+ * Extracts a title from an HTML document.
142
+ *
143
+ * @param html - HTML source snippet.
144
+ * @returns Metadata title, heading, or null.
145
+ *
146
+ * @private utility of Agents Server chat citation labels
147
+ */
148
+ export function extractCitationLabelFromHtml(html: string): string | null {
149
+ const dom = new JSDOM(html);
150
+ const document = dom.window.document;
151
+ const candidates = [
152
+ getMetaContent(document, 'property', 'og:title'),
153
+ getMetaContent(document, 'name', 'twitter:title'),
154
+ document.querySelector('title')?.textContent,
155
+ document.querySelector('h1')?.textContent,
156
+ ];
157
+
158
+ return candidates.map(normalizeResolvedCitationLabel).find((label): label is string => Boolean(label)) || null;
159
+ }
160
+
161
+ /**
162
+ * Extracts a title from plain text or markdown-like document snippets.
163
+ *
164
+ * @param text - Text source snippet.
165
+ * @returns Heading or first useful line.
166
+ *
167
+ * @private utility of Agents Server chat citation labels
168
+ */
169
+ export function extractCitationLabelFromPlainText(text: string): string | null {
170
+ const headingMatch = text.match(MARKDOWN_HEADING_REGEX);
171
+ const heading = normalizeResolvedCitationLabel(headingMatch?.[1]);
172
+ if (heading) {
173
+ return heading;
174
+ }
175
+
176
+ const firstLine = text
177
+ .split(/\r?\n/)
178
+ .map(normalizeResolvedCitationLabel)
179
+ .find((line): line is string => Boolean(line));
180
+
181
+ return firstLine || null;
182
+ }
183
+
184
+ /**
185
+ * Extracts a title from PDF metadata bytes.
186
+ *
187
+ * @param bytes - PDF byte snippet.
188
+ * @returns PDF metadata title or null.
189
+ *
190
+ * @private utility of Agents Server chat citation labels
191
+ */
192
+ export function extractCitationLabelFromPdfBytes(bytes: Uint8Array): string | null {
193
+ const latin1Text = Buffer.from(bytes).toString('latin1');
194
+ const utf8Text = new TextDecoder('utf-8', { fatal: false }).decode(bytes);
195
+ const xmpTitle = extractPdfXmpTitle(utf8Text);
196
+ if (xmpTitle) {
197
+ return xmpTitle;
198
+ }
199
+
200
+ const literalTitleMatch = latin1Text.match(PDF_LITERAL_TITLE_REGEX);
201
+ const literalTitle = normalizeResolvedCitationLabel(
202
+ literalTitleMatch?.[1] ? decodePdfLiteralString(literalTitleMatch[1]) : undefined,
203
+ );
204
+ if (literalTitle) {
205
+ return literalTitle;
206
+ }
207
+
208
+ const hexTitleMatch = latin1Text.match(PDF_HEX_TITLE_REGEX);
209
+ return normalizeResolvedCitationLabel(hexTitleMatch?.[1] ? decodePdfHexString(hexTitleMatch[1]) : undefined);
210
+ }
211
+
212
+ /**
213
+ * Reads at most `maxBytes` bytes from one fetch response.
214
+ *
215
+ * @param response - Fetch response.
216
+ * @param maxBytes - Maximum number of bytes to keep.
217
+ * @returns Collected response bytes.
218
+ *
219
+ * @private utility of Agents Server chat citation labels
220
+ */
221
+ async function readResponseSnippet(response: Response, maxBytes: number): Promise<Uint8Array> {
222
+ if (!response.body) {
223
+ return new Uint8Array(await response.arrayBuffer()).slice(0, maxBytes);
224
+ }
225
+
226
+ const reader = response.body.getReader();
227
+ const chunks: Uint8Array[] = [];
228
+ let collectedBytes = 0;
229
+
230
+ try {
231
+ while (collectedBytes < maxBytes) {
232
+ const { done, value } = await reader.read();
233
+ if (done || !value) {
234
+ break;
235
+ }
236
+
237
+ const remainingBytes = maxBytes - collectedBytes;
238
+ const chunk = value.length > remainingBytes ? value.slice(0, remainingBytes) : value;
239
+ chunks.push(chunk);
240
+ collectedBytes += chunk.length;
241
+ }
242
+
243
+ if (collectedBytes >= maxBytes) {
244
+ await reader.cancel();
245
+ }
246
+ } finally {
247
+ reader.releaseLock();
248
+ }
249
+
250
+ const bytes = new Uint8Array(collectedBytes);
251
+ let offset = 0;
252
+ for (const chunk of chunks) {
253
+ bytes.set(chunk, offset);
254
+ offset += chunk.length;
255
+ }
256
+
257
+ return bytes;
258
+ }
259
+
260
+ /**
261
+ * Extracts the content attribute from a matching metadata tag.
262
+ *
263
+ * @param document - Parsed DOM document.
264
+ * @param attribute - Metadata selector attribute.
265
+ * @param value - Metadata selector value.
266
+ * @returns Metadata content or undefined.
267
+ *
268
+ * @private utility of Agents Server chat citation labels
269
+ */
270
+ function getMetaContent(document: Document, attribute: 'name' | 'property', value: string): string | undefined {
271
+ return document.querySelector(`meta[${attribute}="${value}"]`)?.getAttribute('content') || undefined;
272
+ }
273
+
274
+ /**
275
+ * Normalizes a title candidate into a compact display label.
276
+ *
277
+ * @param label - Raw label candidate.
278
+ * @returns Trimmed label or null.
279
+ *
280
+ * @private utility of Agents Server chat citation labels
281
+ */
282
+ function normalizeResolvedCitationLabel(label: string | null | undefined): string | null {
283
+ const normalized = (label || '').replace(/\s+/g, ' ').trim();
284
+
285
+ return normalized || null;
286
+ }
287
+
288
+ /**
289
+ * Returns a normalized HTTP(S) URL candidate.
290
+ *
291
+ * @param value - URL candidate.
292
+ * @returns URL href or null.
293
+ *
294
+ * @private utility of Agents Server chat citation labels
295
+ */
296
+ function normalizeHttpUrl(value: string | undefined): string | null {
297
+ if (!value) {
298
+ return null;
299
+ }
300
+
301
+ try {
302
+ const url = new URL(value.trim());
303
+ return url.protocol === 'http:' || url.protocol === 'https:' ? url.href : null;
304
+ } catch {
305
+ return null;
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Checks whether bytes start with a PDF file signature.
311
+ *
312
+ * @param bytes - Response bytes.
313
+ * @returns True when the snippet looks like a PDF.
314
+ *
315
+ * @private utility of Agents Server chat citation labels
316
+ */
317
+ function startsWithPdfSignature(bytes: Uint8Array): boolean {
318
+ return bytes.length >= 4 && bytes[0] === 0x25 && bytes[1] === 0x50 && bytes[2] === 0x44 && bytes[3] === 0x46;
319
+ }
320
+
321
+ /**
322
+ * Extracts an XMP title from a PDF text snippet.
323
+ *
324
+ * @param text - UTF-8 decoded PDF snippet.
325
+ * @returns XMP title or null.
326
+ *
327
+ * @private utility of Agents Server chat citation labels
328
+ */
329
+ function extractPdfXmpTitle(text: string): string | null {
330
+ const title =
331
+ text.match(PDF_XMP_TITLE_REGEX)?.[1] || text.match(PDF_XMP_SIMPLE_TITLE_REGEX)?.[1]?.replace(/<[^>]+>/g, ' ');
332
+
333
+ return normalizeResolvedCitationLabel(title ? decodeHtmlText(title) : undefined);
334
+ }
335
+
336
+ /**
337
+ * Decodes text entities using the DOM parser already used for HTML titles.
338
+ *
339
+ * @param value - Raw HTML text.
340
+ * @returns Decoded text.
341
+ *
342
+ * @private utility of Agents Server chat citation labels
343
+ */
344
+ function decodeHtmlText(value: string): string {
345
+ return new JSDOM(`<span>${value}</span>`).window.document.querySelector('span')?.textContent || value;
346
+ }
347
+
348
+ /**
349
+ * Decodes a PDF literal string.
350
+ *
351
+ * @param value - Escaped PDF literal value.
352
+ * @returns Decoded text.
353
+ *
354
+ * @private utility of Agents Server chat citation labels
355
+ */
356
+ function decodePdfLiteralString(value: string): string {
357
+ const decoded = value
358
+ .replace(/\\\r?\n/g, '')
359
+ .replace(/\\([nrtbf()\\])/g, (_match, escaped: string) => {
360
+ const replacements: Record<string, string> = {
361
+ n: '\n',
362
+ r: '\r',
363
+ t: '\t',
364
+ b: '\b',
365
+ f: '\f',
366
+ '(': '(',
367
+ ')': ')',
368
+ '\\': '\\',
369
+ };
370
+
371
+ return replacements[escaped] || escaped;
372
+ })
373
+ .replace(/\\([0-7]{1,3})/g, (_match, octal: string) => String.fromCharCode(parseInt(octal, 8)));
374
+
375
+ return decodePdfBinaryString(decoded);
376
+ }
377
+
378
+ /**
379
+ * Decodes a PDF hexadecimal string.
380
+ *
381
+ * @param value - Hex encoded PDF string.
382
+ * @returns Decoded text.
383
+ *
384
+ * @private utility of Agents Server chat citation labels
385
+ */
386
+ function decodePdfHexString(value: string): string {
387
+ const normalizedHex = value.replace(/\s+/g, '');
388
+ const evenHex = normalizedHex.length % 2 === 0 ? normalizedHex : `${normalizedHex}0`;
389
+ const bytes = new Uint8Array(evenHex.length / 2);
390
+
391
+ for (let index = 0; index < evenHex.length; index += 2) {
392
+ bytes[index / 2] = parseInt(evenHex.slice(index, index + 2), 16);
393
+ }
394
+
395
+ if (bytes[0] === 0xfe && bytes[1] === 0xff) {
396
+ return decodeUtf16BeBytes(bytes.slice(2));
397
+ }
398
+
399
+ return new TextDecoder('utf-8', { fatal: false }).decode(bytes);
400
+ }
401
+
402
+ /**
403
+ * Decodes a binary string that may use a UTF-16BE byte-order mark.
404
+ *
405
+ * @param value - Binary string with one byte per character.
406
+ * @returns Decoded text.
407
+ *
408
+ * @private utility of Agents Server chat citation labels
409
+ */
410
+ function decodePdfBinaryString(value: string): string {
411
+ if (value.charCodeAt(0) === 0xfe && value.charCodeAt(1) === 0xff) {
412
+ const bytes = Uint8Array.from(value.slice(2), (character) => character.charCodeAt(0));
413
+ return decodeUtf16BeBytes(bytes);
414
+ }
415
+
416
+ return value;
417
+ }
418
+
419
+ /**
420
+ * Decodes UTF-16BE bytes into a JavaScript string.
421
+ *
422
+ * @param bytes - Big-endian UTF-16 bytes.
423
+ * @returns Decoded text.
424
+ *
425
+ * @private utility of Agents Server chat citation labels
426
+ */
427
+ function decodeUtf16BeBytes(bytes: Uint8Array): string {
428
+ let text = '';
429
+ for (let index = 0; index + 1 < bytes.length; index += 2) {
430
+ text += String.fromCharCode((bytes[index]! << 8) | bytes[index + 1]!);
431
+ }
432
+
433
+ return text;
434
+ }
435
+
436
+ // Note: [🟢] Code for Agents Server chat citation label resolver should never be published into packages that could be imported into browser environment
@@ -1,3 +1,4 @@
1
+ import { spaceTrim } from 'spacetrim';
1
2
  import type { string_knowledge_source_link } from '../../../../../src/types/typeAliases';
2
3
  import { $provideFilesystemForNode } from '../../../../../src/scrapers/_common/register/$provideFilesystemForNode';
3
4
  import type { ScraperSourceHandler } from '../../../../../src/scrapers/_common/Scraper';
@@ -121,7 +122,15 @@ export async function resolveWebsiteKnowledgeSourcesForServer(
121
122
  continue;
122
123
  }
123
124
 
124
- const inlineSource = createInlineKnowledgeSourceFile(`Source URL: ${source}\n\n${markdown}`);
125
+ const inlineSource = createInlineKnowledgeSourceFile(
126
+ spaceTrim(
127
+ (block) => `
128
+ Source URL: ${source}
129
+
130
+ ${block(markdown)}
131
+ `,
132
+ ),
133
+ );
125
134
  const uploadedSource = await uploadInlineKnowledgeSource(inlineSource);
126
135
  resolvedKnowledgeSources.push(uploadedSource);
127
136
  } catch (error) {
@@ -1,6 +1,8 @@
1
- import { createHmac } from 'crypto';
1
+ import { createHmac, randomBytes } from 'crypto';
2
2
  import { cookies, headers } from 'next/headers';
3
3
  import { cache } from 'react';
4
+ import { spaceTrim } from 'spacetrim';
5
+ import { EnvironmentMismatchError } from '../../../../src/errors/EnvironmentMismatchError';
4
6
  import { isStandaloneVpsRawIpBootstrapActive } from './standaloneVpsRawIpBootstrap';
5
7
 
6
8
  /**
@@ -9,9 +11,64 @@ import { isStandaloneVpsRawIpBootstrapActive } from './standaloneVpsRawIpBootstr
9
11
  export const SESSION_COOKIE_NAME = 'sessionToken';
10
12
 
11
13
  /**
12
- * Secret key used for signing session payloads.
14
+ * Cached HMAC signing key resolved from `SESSION_SECRET` on first use.
15
+ *
16
+ * Computed lazily so module imports never fail eagerly at build time and the
17
+ * environment can still be configured before the first authenticated request.
18
+ */
19
+ let resolvedSessionSigningKey: string | null = null;
20
+
21
+ /**
22
+ * Resolves the HMAC signing key used for session payloads.
23
+ *
24
+ * In production, `SESSION_SECRET` must be configured explicitly — falling back
25
+ * to any shared credential (in particular `ADMIN_PASSWORD`) or a hardcoded
26
+ * literal would let anyone who learns that value forge session tokens for any
27
+ * user (including `admin`). Outside production, a per-process random key is
28
+ * generated on first use so local development works without configuration
29
+ * while still rejecting the unsafe hardcoded default.
30
+ *
31
+ * @returns HMAC signing key.
32
+ * @throws {EnvironmentMismatchError} When `SESSION_SECRET` is missing in production.
13
33
  */
14
- const SECRET_KEY = process.env.ADMIN_PASSWORD || 'default-secret-key-change-me';
34
+ function getSessionSigningKey(): string {
35
+ if (resolvedSessionSigningKey !== null) {
36
+ return resolvedSessionSigningKey;
37
+ }
38
+
39
+ const configuredSecret = process.env.SESSION_SECRET?.trim();
40
+ if (configuredSecret) {
41
+ resolvedSessionSigningKey = configuredSecret;
42
+ return resolvedSessionSigningKey;
43
+ }
44
+
45
+ if (process.env.NODE_ENV === 'production') {
46
+ throw new EnvironmentMismatchError(
47
+ spaceTrim(`
48
+ Missing required \`SESSION_SECRET\` environment variable in production.
49
+
50
+ The Agents Server signs session cookies with HMAC-SHA256 keyed by
51
+ \`SESSION_SECRET\`. Reusing \`ADMIN_PASSWORD\` or a hardcoded fallback
52
+ would let anyone who learns that value forge session tokens for any
53
+ user (including \`admin\`).
54
+
55
+ **Fix:** set \`SESSION_SECRET\` to a long random string (for example
56
+ the output of \`openssl rand -hex 32\`) in the deployment environment
57
+ and restart the server.
58
+ `),
59
+ );
60
+ }
61
+
62
+ resolvedSessionSigningKey = randomBytes(32).toString('hex');
63
+ console.warn(
64
+ spaceTrim(`
65
+ \`SESSION_SECRET\` is not configured — generated a random per-process
66
+ signing key for this non-production run. Existing session cookies will
67
+ be invalidated on every restart until \`SESSION_SECRET\` is set.
68
+ `),
69
+ );
70
+ return resolvedSessionSigningKey;
71
+ }
15
72
 
16
73
  /**
17
74
  * Signed session payload persisted in the authentication cookie.
@@ -73,7 +130,7 @@ export type SessionCookieSecurityContext = {
73
130
  */
74
131
  export function serializeSessionToken(user: SessionUser): string {
75
132
  const payload = JSON.stringify(user);
76
- const signature = createHmac('sha256', SECRET_KEY).update(payload).digest('hex');
133
+ const signature = createHmac('sha256', getSessionSigningKey()).update(payload).digest('hex');
77
134
  return `${Buffer.from(payload).toString('base64')}.${signature}`;
78
135
  }
79
136
 
@@ -94,7 +151,7 @@ export function parseSessionToken(token: string | null | undefined): SessionUser
94
151
  }
95
152
 
96
153
  const payload = Buffer.from(payloadBase64, 'base64').toString('utf-8');
97
- const expectedSignature = createHmac('sha256', SECRET_KEY).update(payload).digest('hex');
154
+ const expectedSignature = createHmac('sha256', getSessionSigningKey()).update(payload).digest('hex');
98
155
 
99
156
  if (signature !== expectedSignature) {
100
157
  return null;