@promptbook/cli 0.112.0-111 → 0.112.0-113

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 (96) hide show
  1. package/README.md +7 -7
  2. package/apps/agents-server/README.md +1 -1
  3. package/apps/agents-server/next.config.ts +20 -1
  4. package/apps/agents-server/src/app/admin/servers/CreateServerDialog.tsx +16 -0
  5. package/apps/agents-server/src/app/admin/servers/useCreateServerWizard.ts +31 -5
  6. package/apps/agents-server/src/app/api/admin/code-runners/route.ts +1 -1
  7. package/apps/agents-server/src/app/api/admin/servers/route.ts +5 -0
  8. package/apps/agents-server/src/app/api/internal/user-chat-jobs/run/route.ts +1 -1
  9. package/apps/agents-server/src/app/api/metadata/route.ts +4 -0
  10. package/apps/agents-server/src/database/customJavascript.ts +62 -1
  11. package/apps/agents-server/src/database/customStylesheet.ts +60 -1
  12. package/apps/agents-server/src/database/getMetadata.ts +84 -3
  13. package/apps/agents-server/src/instrumentation-client.ts +28 -0
  14. package/apps/agents-server/src/instrumentation.ts +19 -0
  15. package/apps/agents-server/src/sentry.edge.config.ts +18 -0
  16. package/apps/agents-server/src/sentry.server.config.ts +19 -0
  17. package/apps/agents-server/src/utils/codeRunnerConfiguration.ts +1 -1
  18. package/apps/agents-server/src/utils/errorReporting/agentsServerSentryContext.ts +203 -0
  19. package/apps/agents-server/src/utils/errorReporting/registerServerErrorSentryLogging.ts +381 -0
  20. package/apps/agents-server/src/utils/errorReporting/sendApplicationErrorReportToSentry.ts +43 -152
  21. package/apps/agents-server/src/utils/errorReporting/sentrySdkConfig.ts +237 -0
  22. package/apps/agents-server/src/utils/errorReporting/sentryStore.ts +187 -0
  23. package/apps/agents-server/src/utils/externalChatRunner/createExternalAgentRepositoryFiles.ts +2 -2
  24. package/apps/agents-server/src/utils/serverManagement/createManagedServer/bootstrapManagedServer.ts +3 -1
  25. package/apps/agents-server/src/utils/serverManagement/createManagedServer/normalizeCreateServerInput.ts +6 -0
  26. package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerDefaultAgents.ts +7 -3
  27. package/apps/agents-server/src/utils/serverManagement/createManagedServer.ts +5 -0
  28. package/apps/agents-server/src/utils/userChat/listUserChats.ts +109 -0
  29. package/apps/agents-server/src/utils/userChat/triggerUserChatJobWorker.ts +54 -19
  30. package/apps/agents-server/src/utils/vpsConfiguration.ts +1 -1
  31. package/esm/index.es.js +9256 -8795
  32. package/esm/index.es.js.map +1 -1
  33. package/esm/scripts/run-agent-chat/executeAgentChatTurn.d.ts +28 -0
  34. package/esm/scripts/run-agent-chat/runAgentChat.d.ts +5 -0
  35. package/esm/scripts/run-agent-chat/runAgentExec.d.ts +11 -0
  36. package/esm/scripts/run-agent-messages/messages/createAgentRunnerSystemMessage.d.ts +10 -0
  37. package/esm/scripts/run-codex-prompts/common/resolveInlineOrFileText.d.ts +14 -0
  38. package/esm/src/_packages/node.index.d.ts +20 -0
  39. package/esm/src/_packages/types.index.d.ts +16 -0
  40. package/esm/src/book-3.0/BookNodeAgentSource.d.ts +38 -0
  41. package/esm/src/book-3.0/CliAgent.d.ts +68 -0
  42. package/esm/src/book-3.0/CliAgent.test.d.ts +1 -0
  43. package/esm/src/book-3.0/LiteAgent.d.ts +68 -0
  44. package/esm/src/book-3.0/LiteAgent.test.d.ts +1 -0
  45. package/esm/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.d.ts +12 -0
  46. package/esm/src/cli/cli-commands/agent/agentCliOptions.d.ts +29 -0
  47. package/esm/src/cli/cli-commands/agent/chat.d.ts +10 -0
  48. package/esm/src/cli/cli-commands/agent/exec.d.ts +10 -0
  49. package/esm/src/cli/cli-commands/agent/run.test.d.ts +1 -0
  50. package/esm/src/cli/cli-commands/agent.d.ts +14 -0
  51. package/esm/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +2 -2
  52. package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +9 -9
  53. package/esm/src/version.d.ts +1 -1
  54. package/package.json +2 -1
  55. package/src/_packages/node.index.ts +20 -0
  56. package/src/_packages/types.index.ts +16 -0
  57. package/src/book-3.0/BookNodeAgentSource.ts +135 -0
  58. package/src/book-3.0/CliAgent.ts +236 -0
  59. package/src/book-3.0/LiteAgent.ts +463 -0
  60. package/src/book-components/BookEditor/BookEditor.module.css +61 -0
  61. package/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.tsx +74 -0
  62. package/src/book-components/BookEditor/BookEditorActionbar.tsx +3 -3
  63. package/src/cli/cli-commands/agent/agentCliOptions.ts +63 -0
  64. package/src/cli/cli-commands/agent/chat.ts +54 -0
  65. package/src/cli/cli-commands/agent/exec.ts +60 -0
  66. package/src/cli/cli-commands/agent.ts +45 -0
  67. package/src/cli/cli-commands/agents-server/startAgentsServer.ts +2 -2
  68. package/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts +1 -1
  69. package/src/cli/cli-commands/common/promptRunnerCliOptions.ts +27 -23
  70. package/src/cli/promptbookCli.ts +2 -0
  71. package/src/other/templates/getTemplatesPipelineCollection.ts +809 -901
  72. package/src/version.ts +2 -2
  73. package/src/versions.txt +2 -0
  74. package/umd/index.umd.js +9255 -8794
  75. package/umd/index.umd.js.map +1 -1
  76. package/umd/scripts/run-agent-chat/executeAgentChatTurn.d.ts +28 -0
  77. package/umd/scripts/run-agent-chat/runAgentChat.d.ts +5 -0
  78. package/umd/scripts/run-agent-chat/runAgentExec.d.ts +11 -0
  79. package/umd/scripts/run-agent-messages/messages/createAgentRunnerSystemMessage.d.ts +10 -0
  80. package/umd/scripts/run-codex-prompts/common/resolveInlineOrFileText.d.ts +14 -0
  81. package/umd/src/_packages/node.index.d.ts +20 -0
  82. package/umd/src/_packages/types.index.d.ts +16 -0
  83. package/umd/src/book-3.0/BookNodeAgentSource.d.ts +38 -0
  84. package/umd/src/book-3.0/CliAgent.d.ts +68 -0
  85. package/umd/src/book-3.0/CliAgent.test.d.ts +1 -0
  86. package/umd/src/book-3.0/LiteAgent.d.ts +68 -0
  87. package/umd/src/book-3.0/LiteAgent.test.d.ts +1 -0
  88. package/umd/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.d.ts +12 -0
  89. package/umd/src/cli/cli-commands/agent/agentCliOptions.d.ts +29 -0
  90. package/umd/src/cli/cli-commands/agent/chat.d.ts +10 -0
  91. package/umd/src/cli/cli-commands/agent/exec.d.ts +10 -0
  92. package/umd/src/cli/cli-commands/agent/run.test.d.ts +1 -0
  93. package/umd/src/cli/cli-commands/agent.d.ts +14 -0
  94. package/umd/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +2 -2
  95. package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +9 -9
  96. package/umd/src/version.d.ts +1 -1
@@ -1,146 +1,18 @@
1
+ import * as Sentry from '@sentry/nextjs';
1
2
  import type { ApplicationErrorReportPayload } from './applicationErrorHandling';
2
-
3
- /**
4
- * Sentry protocol version used for store endpoint requests.
5
- */
6
- const SENTRY_PROTOCOL_VERSION = '7';
7
-
8
- /**
9
- * Endpoint content type used when sending JSON payloads to Sentry.
10
- */
11
- const JSON_CONTENT_TYPE = 'application/json';
3
+ import { enrichSentryStorePayloadWithAgentsServerContext } from './agentsServerSentryContext';
4
+ import {
5
+ createSentryTimestamp,
6
+ resolveRequiredSentryDsn,
7
+ sendSentryStorePayload,
8
+ type SentryStorePayload,
9
+ } from './sentryStore';
12
10
 
13
11
  /**
14
12
  * Logger name visible in Sentry events.
15
13
  */
16
14
  const SENTRY_APPLICATION_ERROR_LOGGER = 'agents-server.application-error';
17
15
 
18
- /**
19
- * Number of milliseconds in one second.
20
- */
21
- const MILLISECONDS_IN_SECOND = 1000;
22
-
23
- /**
24
- * Minimal Sentry DSN parts needed for store API requests.
25
- */
26
- type SentryDsnParts = {
27
- /**
28
- * Store endpoint derived from DSN host/project.
29
- */
30
- storeEndpoint: URL;
31
- };
32
-
33
- /**
34
- * Payload shape submitted to the Sentry store endpoint.
35
- */
36
- type SentryStorePayload = {
37
- /**
38
- * Event platform.
39
- */
40
- platform: 'javascript';
41
-
42
- /**
43
- * Event level.
44
- */
45
- level: 'error';
46
-
47
- /**
48
- * Event logger value.
49
- */
50
- logger: string;
51
-
52
- /**
53
- * Seconds since UNIX epoch.
54
- */
55
- timestamp: number;
56
-
57
- /**
58
- * Human readable message.
59
- */
60
- message: string;
61
-
62
- /**
63
- * Server/deployment name.
64
- */
65
- server_name: string;
66
-
67
- /**
68
- * Diagnostic tags used for filtering.
69
- */
70
- tags: Record<string, string>;
71
-
72
- /**
73
- * Structured exception details.
74
- */
75
- exception: {
76
- /**
77
- * Individual exception list.
78
- */
79
- values: Array<{
80
- /**
81
- * Exception type.
82
- */
83
- type: string;
84
-
85
- /**
86
- * Exception message.
87
- */
88
- value: string;
89
- }>;
90
- };
91
-
92
- /**
93
- * Additional diagnostic payload.
94
- */
95
- extra: Record<string, string | null>;
96
- };
97
-
98
- /**
99
- * Resolves Sentry DSN from environment.
100
- *
101
- * @returns Raw DSN string.
102
- * @throws Error when DSN is missing.
103
- */
104
- function resolveSentryDsn(): string {
105
- const dsn = process.env.SENTRY_DSN ?? process.env.NEXT_PUBLIC_SENTRY_DSN;
106
- if (!dsn) {
107
- throw new Error('Missing Sentry DSN. Configure SENTRY_DSN or NEXT_PUBLIC_SENTRY_DSN.');
108
- }
109
-
110
- return dsn;
111
- }
112
-
113
- /**
114
- * Parses a DSN into pieces required for Sentry store API requests.
115
- *
116
- * @param dsn - Raw Sentry DSN.
117
- * @returns Parsed DSN details.
118
- * @throws Error when DSN format is invalid.
119
- */
120
- function parseSentryDsn(dsn: string): SentryDsnParts {
121
- const dsnUrl = new URL(dsn);
122
- const pathSegments = dsnUrl.pathname.split('/').filter(Boolean);
123
- const projectId = pathSegments.at(-1);
124
- const pathPrefix = pathSegments.slice(0, -1).join('/');
125
-
126
- if (!projectId) {
127
- throw new Error('Invalid Sentry DSN: missing project ID.');
128
- }
129
-
130
- if (!dsnUrl.username) {
131
- throw new Error('Invalid Sentry DSN: missing public key.');
132
- }
133
-
134
- const basePath = pathPrefix ? `/${pathPrefix}` : '';
135
- const storeEndpoint = new URL(`${dsnUrl.protocol}//${dsnUrl.host}${basePath}/api/${projectId}/store/`);
136
- storeEndpoint.searchParams.set('sentry_key', dsnUrl.username);
137
- storeEndpoint.searchParams.set('sentry_version', SENTRY_PROTOCOL_VERSION);
138
-
139
- return {
140
- storeEndpoint,
141
- };
142
- }
143
-
144
16
  /**
145
17
  * Creates the Sentry-compatible JSON payload from the application report.
146
18
  *
@@ -148,11 +20,11 @@ function parseSentryDsn(dsn: string): SentryDsnParts {
148
20
  * @returns Sentry store payload.
149
21
  */
150
22
  function createSentryStorePayload(report: ApplicationErrorReportPayload): SentryStorePayload {
151
- return {
23
+ return enrichSentryStorePayloadWithAgentsServerContext({
152
24
  platform: 'javascript',
153
25
  level: 'error',
154
26
  logger: SENTRY_APPLICATION_ERROR_LOGGER,
155
- timestamp: Date.now() / MILLISECONDS_IN_SECOND,
27
+ timestamp: createSentryTimestamp(),
156
28
  message: report.errorMessage,
157
29
  server_name: report.serverName,
158
30
  tags: {
@@ -174,7 +46,7 @@ function createSentryStorePayload(report: ApplicationErrorReportPayload): Sentry
174
46
  pageUrl: report.pageUrl ?? null,
175
47
  reportedAt: report.reportedAt,
176
48
  },
177
- };
49
+ });
178
50
  }
179
51
 
180
52
  /**
@@ -183,20 +55,39 @@ function createSentryStorePayload(report: ApplicationErrorReportPayload): Sentry
183
55
  * @param report - Structured browser report payload.
184
56
  */
185
57
  export async function sendApplicationErrorReportToSentry(report: ApplicationErrorReportPayload): Promise<void> {
186
- const dsn = resolveSentryDsn();
187
- const { storeEndpoint } = parseSentryDsn(dsn);
188
- const payload = createSentryStorePayload(report);
58
+ const sentryPayload = createSentryStorePayload(report);
59
+
60
+ if (Sentry.getClient()) {
61
+ Sentry.captureException(createApplicationErrorSdkException(report, sentryPayload), {
62
+ level: sentryPayload.level,
63
+ tags: sentryPayload.tags,
64
+ extra: sentryPayload.extra,
65
+ });
66
+ await Sentry.flush(2000);
67
+ return;
68
+ }
189
69
 
190
- const response = await fetch(storeEndpoint, {
191
- method: 'POST',
192
- headers: {
193
- 'Content-Type': JSON_CONTENT_TYPE,
194
- },
195
- body: JSON.stringify(payload),
196
- });
70
+ await sendSentryStorePayload(sentryPayload, resolveRequiredSentryDsn());
71
+ }
197
72
 
198
- if (!response.ok) {
199
- const responseBody = await response.text();
200
- throw new Error(`Sentry rejected application error report (${response.status}): ${responseBody}`);
73
+ /**
74
+ * Rebuilds an `Error` object from a browser application report so the SDK can parse stack frames.
75
+ *
76
+ * @param report - Browser-generated application error report.
77
+ * @param sentryPayload - Structured Sentry payload created from the report.
78
+ * @returns Error object with the original browser stack when available.
79
+ */
80
+ function createApplicationErrorSdkException(
81
+ report: ApplicationErrorReportPayload,
82
+ sentryPayload: SentryStorePayload,
83
+ ): Error {
84
+ const error = new Error(sentryPayload.message);
85
+
86
+ error.name = report.errorName || 'Error';
87
+
88
+ if (report.errorStack?.trim()) {
89
+ error.stack = report.errorStack;
201
90
  }
91
+
92
+ return error;
202
93
  }
@@ -0,0 +1,237 @@
1
+ import { BOOK_LANGUAGE_VERSION, PROMPTBOOK_ENGINE_VERSION } from '../../../../../src/version';
2
+
3
+ /**
4
+ * Default Sentry project DSN for Promptbook Agents Server telemetry.
5
+ *
6
+ * @private internal Sentry SDK configuration for Agents Server
7
+ */
8
+ export const DEFAULT_SENTRY_DSN =
9
+ 'https://986f734e9cddaeeec33e2a360f7d0b62@o4508778158030848.ingest.de.sentry.io/4511534509785168';
10
+
11
+ /**
12
+ * Sentry organization slug used by build-time SDK tooling.
13
+ *
14
+ * @private internal Sentry SDK configuration for Agents Server
15
+ */
16
+ export const SENTRY_ORGANIZATION = 'promptbook';
17
+
18
+ /**
19
+ * Sentry project slug used by build-time SDK tooling.
20
+ *
21
+ * @private internal Sentry SDK configuration for Agents Server
22
+ */
23
+ export const SENTRY_PROJECT = 's22';
24
+
25
+ /**
26
+ * Performance tracing sample rate requested for the Agents Server.
27
+ *
28
+ * @private internal Sentry SDK configuration for Agents Server
29
+ */
30
+ export const SENTRY_TRACES_SAMPLE_RATE = 1.0;
31
+
32
+ /**
33
+ * Browser-side targets that should receive distributed tracing headers.
34
+ *
35
+ * @private internal Sentry SDK configuration for Agents Server
36
+ */
37
+ export const SENTRY_TRACE_PROPAGATION_TARGETS: Array<string | RegExp> = [
38
+ 'localhost',
39
+ /^\/api\//,
40
+ /^https:\/\/.*\.ptbk\.io\/api\//,
41
+ /^https:\/\/.*\.vercel\.app\/api\//,
42
+ ];
43
+
44
+ /**
45
+ * Release name used for Agents Server Sentry events.
46
+ */
47
+ const AGENTS_SERVER_SENTRY_RELEASE_NAME = 'promptbook-agents-server';
48
+
49
+ /**
50
+ * Fallback text used in Sentry tags when a value is not configured.
51
+ */
52
+ const UNKNOWN_SENTRY_CONTEXT_VALUE = 'unknown';
53
+
54
+ /**
55
+ * Maximum length kept for Sentry tag values.
56
+ */
57
+ const MAX_SENTRY_TAG_VALUE_LENGTH = 200;
58
+
59
+ /**
60
+ * Resolves the browser-safe Sentry DSN.
61
+ *
62
+ * @returns Browser-safe Sentry DSN.
63
+ *
64
+ * @private internal Sentry SDK configuration for Agents Server
65
+ */
66
+ export function resolveBrowserSentryDsn(): string {
67
+ return process.env.NEXT_PUBLIC_SENTRY_DSN ?? DEFAULT_SENTRY_DSN;
68
+ }
69
+
70
+ /**
71
+ * Resolves the server-side Sentry DSN.
72
+ *
73
+ * @returns Server-side Sentry DSN.
74
+ *
75
+ * @private internal Sentry SDK configuration for Agents Server
76
+ */
77
+ export function resolveServerSentryDsn(): string {
78
+ return process.env.SENTRY_DSN ?? resolveBrowserSentryDsn();
79
+ }
80
+
81
+ /**
82
+ * Resolves the most specific deployment environment name available.
83
+ *
84
+ * @returns Environment name suitable for Sentry.
85
+ *
86
+ * @private internal Sentry SDK configuration for Agents Server
87
+ */
88
+ export function resolveSentrySdkEnvironment(): string {
89
+ return (
90
+ getFirstNonEmptyString(
91
+ process.env.SENTRY_ENVIRONMENT,
92
+ process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV,
93
+ process.env.VERCEL_TARGET_ENV,
94
+ process.env.NEXT_PUBLIC_VERCEL_ENV,
95
+ process.env.VERCEL_ENV,
96
+ process.env.PROMPTBOOK_REPOSITORY_REF,
97
+ process.env.NODE_ENV,
98
+ ) ?? UNKNOWN_SENTRY_CONTEXT_VALUE
99
+ );
100
+ }
101
+
102
+ /**
103
+ * Resolves the current deployment commit hash from common hosting environment variables.
104
+ *
105
+ * @returns Commit hash or null when unavailable.
106
+ *
107
+ * @private internal Sentry SDK configuration for Agents Server
108
+ */
109
+ export function resolveSentrySdkCommitHash(): string | null {
110
+ return getFirstNonEmptyString(
111
+ process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA,
112
+ process.env.VERCEL_GIT_COMMIT_SHA,
113
+ process.env.PROMPTBOOK_COMMIT_SHA,
114
+ process.env.GIT_COMMIT_SHA,
115
+ process.env.COMMIT_SHA,
116
+ process.env.SOURCE_VERSION,
117
+ );
118
+ }
119
+
120
+ /**
121
+ * Resolves the current repository branch or deployment ref.
122
+ *
123
+ * @returns Repository branch/ref or null when unavailable.
124
+ *
125
+ * @private internal Sentry SDK configuration for Agents Server
126
+ */
127
+ export function resolveSentrySdkRepositoryBranch(): string | null {
128
+ return getFirstNonEmptyString(
129
+ process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF,
130
+ process.env.VERCEL_GIT_COMMIT_REF,
131
+ process.env.PROMPTBOOK_REPOSITORY_REF,
132
+ process.env.GIT_BRANCH,
133
+ process.env.BRANCH_NAME,
134
+ );
135
+ }
136
+
137
+ /**
138
+ * Resolves the Sentry release name for the current Agents Server build.
139
+ *
140
+ * @returns Release identifier suitable for Sentry.
141
+ *
142
+ * @private internal Sentry SDK configuration for Agents Server
143
+ */
144
+ export function resolveSentrySdkRelease(): string {
145
+ const appPackageVersion = normalizeOptionalString(process.env.npm_package_version) ?? PROMPTBOOK_ENGINE_VERSION;
146
+ const commitHash = resolveSentrySdkCommitHash();
147
+
148
+ if (!commitHash) {
149
+ return `${AGENTS_SERVER_SENTRY_RELEASE_NAME}@${appPackageVersion}`;
150
+ }
151
+
152
+ return `${AGENTS_SERVER_SENTRY_RELEASE_NAME}@${appPackageVersion}+${commitHash}`;
153
+ }
154
+
155
+ /**
156
+ * Creates shared filterable Sentry tags for Agents Server events.
157
+ *
158
+ * @returns Sanitized Sentry tags.
159
+ *
160
+ * @private internal Sentry SDK configuration for Agents Server
161
+ */
162
+ export function createSentrySdkTags(): Record<string, string> {
163
+ return createSentryTags({
164
+ promptbookEngineVersion: PROMPTBOOK_ENGINE_VERSION,
165
+ bookLanguageVersion: BOOK_LANGUAGE_VERSION,
166
+ appPackageVersion: normalizeOptionalString(process.env.npm_package_version) ?? PROMPTBOOK_ENGINE_VERSION,
167
+ commitHash: resolveSentrySdkCommitHash(),
168
+ repositoryBranch: resolveSentrySdkRepositoryBranch(),
169
+ deploymentEnvironment: resolveSentrySdkEnvironment(),
170
+ vercelEnvironment: getFirstNonEmptyString(process.env.NEXT_PUBLIC_VERCEL_ENV, process.env.VERCEL_ENV),
171
+ targetEnvironment: getFirstNonEmptyString(
172
+ process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV,
173
+ process.env.VERCEL_TARGET_ENV,
174
+ ),
175
+ nextRuntime: process.env.NEXT_RUNTIME,
176
+ nodeEnvironment: process.env.NODE_ENV,
177
+ vercelRegion: process.env.VERCEL_REGION,
178
+ });
179
+ }
180
+
181
+ /**
182
+ * Creates Sentry tag values, preserving missing values as explicit `unknown` markers.
183
+ *
184
+ * @param values - Raw tag values.
185
+ * @returns Sanitized Sentry tags.
186
+ */
187
+ function createSentryTags(values: Record<string, string | null | undefined>): Record<string, string> {
188
+ return Object.fromEntries(
189
+ Object.entries(values).map(([key, value]) => [key, sanitizeSentryTagValue(value)]),
190
+ ) as Record<string, string>;
191
+ }
192
+
193
+ /**
194
+ * Normalizes one value for use as a Sentry tag.
195
+ *
196
+ * @param value - Optional tag value.
197
+ * @returns Non-empty bounded tag value.
198
+ */
199
+ function sanitizeSentryTagValue(value: string | null | undefined): string {
200
+ const normalizedValue = normalizeOptionalString(value) ?? UNKNOWN_SENTRY_CONTEXT_VALUE;
201
+
202
+ return normalizedValue.replace(/\s+/gu, ' ').slice(0, MAX_SENTRY_TAG_VALUE_LENGTH);
203
+ }
204
+
205
+ /**
206
+ * Returns the first non-empty string from a list.
207
+ *
208
+ * @param values - Candidate values in priority order.
209
+ * @returns First trimmed string or null.
210
+ */
211
+ function getFirstNonEmptyString(...values: Array<string | null | undefined>): string | null {
212
+ for (const value of values) {
213
+ const normalizedValue = normalizeOptionalString(value);
214
+
215
+ if (normalizedValue) {
216
+ return normalizedValue;
217
+ }
218
+ }
219
+
220
+ return null;
221
+ }
222
+
223
+ /**
224
+ * Trims one optional environment value.
225
+ *
226
+ * @param value - Raw optional value.
227
+ * @returns Trimmed string or null.
228
+ */
229
+ function normalizeOptionalString(value: string | null | undefined): string | null {
230
+ if (typeof value !== 'string') {
231
+ return null;
232
+ }
233
+
234
+ const normalizedValue = value.trim();
235
+
236
+ return normalizedValue || null;
237
+ }
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Sentry protocol version used for store endpoint requests.
3
+ */
4
+ const SENTRY_PROTOCOL_VERSION = '7';
5
+
6
+ /**
7
+ * Endpoint content type used when sending JSON payloads to Sentry.
8
+ */
9
+ const JSON_CONTENT_TYPE = 'application/json';
10
+
11
+ /**
12
+ * Number of milliseconds in one second.
13
+ */
14
+ const MILLISECONDS_IN_SECOND = 1000;
15
+
16
+ /**
17
+ * Minimal Sentry DSN parts needed for store endpoint requests.
18
+ */
19
+ type SentryDsnParts = {
20
+ /**
21
+ * Store endpoint derived from DSN host/project.
22
+ */
23
+ storeEndpoint: URL;
24
+ };
25
+
26
+ /**
27
+ * Payload shape submitted to the Sentry store endpoint.
28
+ */
29
+ export type SentryStorePayload = {
30
+ /**
31
+ * Event platform.
32
+ */
33
+ platform: 'javascript';
34
+
35
+ /**
36
+ * Event level.
37
+ */
38
+ level: 'error';
39
+
40
+ /**
41
+ * Logger name visible in Sentry events.
42
+ */
43
+ logger: string;
44
+
45
+ /**
46
+ * Seconds since UNIX epoch.
47
+ */
48
+ timestamp: number;
49
+
50
+ /**
51
+ * Human readable message.
52
+ */
53
+ message: string;
54
+
55
+ /**
56
+ * Sentry release identifier.
57
+ */
58
+ release?: string;
59
+
60
+ /**
61
+ * Deployment environment such as production, preview, or staging.
62
+ */
63
+ environment?: string;
64
+
65
+ /**
66
+ * Server/deployment name.
67
+ */
68
+ server_name: string;
69
+
70
+ /**
71
+ * Optional diagnostic tags used for filtering.
72
+ */
73
+ tags?: Record<string, string>;
74
+
75
+ /**
76
+ * Optional structured exception details.
77
+ */
78
+ exception?: {
79
+ /**
80
+ * Individual exception list.
81
+ */
82
+ values: Array<{
83
+ /**
84
+ * Exception type.
85
+ */
86
+ type: string;
87
+
88
+ /**
89
+ * Exception message.
90
+ */
91
+ value: string;
92
+ }>;
93
+ };
94
+
95
+ /**
96
+ * Optional additional diagnostic payload.
97
+ */
98
+ extra?: Record<string, unknown>;
99
+ };
100
+
101
+ /**
102
+ * Resolves Sentry DSN from environment when configured.
103
+ *
104
+ * @returns Raw DSN string or `null` when telemetry is not configured.
105
+ */
106
+ export function resolveOptionalSentryDsn(): string | null {
107
+ return process.env.SENTRY_DSN ?? process.env.NEXT_PUBLIC_SENTRY_DSN ?? null;
108
+ }
109
+
110
+ /**
111
+ * Resolves Sentry DSN from environment.
112
+ *
113
+ * @returns Raw DSN string.
114
+ * @throws Error when DSN is missing.
115
+ */
116
+ export function resolveRequiredSentryDsn(): string {
117
+ const sentryDsn = resolveOptionalSentryDsn();
118
+
119
+ if (!sentryDsn) {
120
+ throw new Error('Missing Sentry DSN. Configure SENTRY_DSN or NEXT_PUBLIC_SENTRY_DSN.');
121
+ }
122
+
123
+ return sentryDsn;
124
+ }
125
+
126
+ /**
127
+ * Parses a DSN into pieces required for Sentry store API requests.
128
+ *
129
+ * @param sentryDsn - Raw Sentry DSN.
130
+ * @returns Parsed DSN details.
131
+ * @throws Error when DSN format is invalid.
132
+ */
133
+ function parseSentryDsn(sentryDsn: string): SentryDsnParts {
134
+ const sentryDsnUrl = new URL(sentryDsn);
135
+ const pathSegments = sentryDsnUrl.pathname.split('/').filter(Boolean);
136
+ const projectId = pathSegments.at(-1);
137
+ const pathPrefix = pathSegments.slice(0, -1).join('/');
138
+
139
+ if (!projectId) {
140
+ throw new Error('Invalid Sentry DSN: missing project ID.');
141
+ }
142
+
143
+ if (!sentryDsnUrl.username) {
144
+ throw new Error('Invalid Sentry DSN: missing public key.');
145
+ }
146
+
147
+ const basePath = pathPrefix ? `/${pathPrefix}` : '';
148
+ const storeEndpoint = new URL(`${sentryDsnUrl.protocol}//${sentryDsnUrl.host}${basePath}/api/${projectId}/store/`);
149
+ storeEndpoint.searchParams.set('sentry_key', sentryDsnUrl.username);
150
+ storeEndpoint.searchParams.set('sentry_version', SENTRY_PROTOCOL_VERSION);
151
+
152
+ return {
153
+ storeEndpoint,
154
+ };
155
+ }
156
+
157
+ /**
158
+ * Creates a current Sentry timestamp expressed in seconds.
159
+ *
160
+ * @returns Current event timestamp in seconds.
161
+ */
162
+ export function createSentryTimestamp(): number {
163
+ return Date.now() / MILLISECONDS_IN_SECOND;
164
+ }
165
+
166
+ /**
167
+ * Sends one Sentry store payload.
168
+ *
169
+ * @param payload - Fully constructed Sentry store payload.
170
+ * @param sentryDsn - Raw Sentry DSN used to derive the store endpoint.
171
+ */
172
+ export async function sendSentryStorePayload(payload: SentryStorePayload, sentryDsn: string): Promise<void> {
173
+ const { storeEndpoint } = parseSentryDsn(sentryDsn);
174
+
175
+ const response = await fetch(storeEndpoint, {
176
+ method: 'POST',
177
+ headers: {
178
+ 'Content-Type': JSON_CONTENT_TYPE,
179
+ },
180
+ body: JSON.stringify(payload),
181
+ });
182
+
183
+ if (!response.ok) {
184
+ const responseBody = await response.text();
185
+ throw new Error(`Sentry rejected error report (${response.status}): ${responseBody}`);
186
+ }
187
+ }
@@ -53,9 +53,9 @@ function createExternalAgentRepositoryPackageJson(): string {
53
53
  scripts: {
54
54
  start: 'npm run agent:run',
55
55
  'agent:run':
56
- 'npx ptbk agent-folder run-agent --agent github-copilot --model gpt-5.4 --thinking-level high --auto-pull --auto-push',
56
+ 'npx ptbk agent-folder run-agent --harness github-copilot --model gpt-5.4 --thinking-level high --auto-pull --auto-push',
57
57
  'agent-local:run':
58
- 'ts-node ../../promptbook/src/cli/test/ptbk.ts agent-folder run-agent --agent github-copilot --model gpt-5.4 --auto-pull --auto-push',
58
+ 'ts-node ../../promptbook/src/cli/test/ptbk.ts agent-folder run-agent --harness github-copilot --model gpt-5.4 --auto-pull --auto-push',
59
59
  },
60
60
  },
61
61
  null,
@@ -47,7 +47,9 @@ export async function bootstrapManagedServer(input: NormalizedCreateServerInput)
47
47
  await applyManagedServerMigrations(client, input, sqlRecorder, migrationLogger);
48
48
  await seedServerUsers(client, input, sqlRecorder);
49
49
  await seedServerMetadata(client, input, sqlRecorder);
50
- await seedServerDefaultAgents(client, input, sqlRecorder);
50
+ if (input.isDefaultAgentsInstalled) {
51
+ await seedServerDefaultAgents(client, input, sqlRecorder);
52
+ }
51
53
 
52
54
  await client.query('COMMIT');
53
55
  sqlRecorder.addStatement('COMMIT');
@@ -51,6 +51,11 @@ export type NormalizedCreateServerInput = {
51
51
  * Initial metadata rows inserted during bootstrap.
52
52
  */
53
53
  readonly metadataEntries: ReadonlyArray<ServerMetadataSeedEntry>;
54
+
55
+ /**
56
+ * Whether bundled default agents from `agents/default` should be created during bootstrap.
57
+ */
58
+ readonly isDefaultAgentsInstalled: boolean;
54
59
  };
55
60
 
56
61
  /**
@@ -103,5 +108,6 @@ export function normalizeCreateServerInput(input: CreateServerInput): Normalized
103
108
  iconUrl,
104
109
  initialSettings: input.initialSettings,
105
110
  }),
111
+ isDefaultAgentsInstalled: input.isDefaultAgentsInstalled !== false,
106
112
  };
107
113
  }