@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.
- package/README.md +7 -7
- package/apps/agents-server/README.md +1 -1
- package/apps/agents-server/next.config.ts +20 -1
- package/apps/agents-server/src/app/admin/servers/CreateServerDialog.tsx +16 -0
- package/apps/agents-server/src/app/admin/servers/useCreateServerWizard.ts +31 -5
- package/apps/agents-server/src/app/api/admin/code-runners/route.ts +1 -1
- package/apps/agents-server/src/app/api/admin/servers/route.ts +5 -0
- package/apps/agents-server/src/app/api/internal/user-chat-jobs/run/route.ts +1 -1
- package/apps/agents-server/src/app/api/metadata/route.ts +4 -0
- package/apps/agents-server/src/database/customJavascript.ts +62 -1
- package/apps/agents-server/src/database/customStylesheet.ts +60 -1
- package/apps/agents-server/src/database/getMetadata.ts +84 -3
- package/apps/agents-server/src/instrumentation-client.ts +28 -0
- package/apps/agents-server/src/instrumentation.ts +19 -0
- package/apps/agents-server/src/sentry.edge.config.ts +18 -0
- package/apps/agents-server/src/sentry.server.config.ts +19 -0
- package/apps/agents-server/src/utils/codeRunnerConfiguration.ts +1 -1
- package/apps/agents-server/src/utils/errorReporting/agentsServerSentryContext.ts +203 -0
- package/apps/agents-server/src/utils/errorReporting/registerServerErrorSentryLogging.ts +381 -0
- package/apps/agents-server/src/utils/errorReporting/sendApplicationErrorReportToSentry.ts +43 -152
- package/apps/agents-server/src/utils/errorReporting/sentrySdkConfig.ts +237 -0
- package/apps/agents-server/src/utils/errorReporting/sentryStore.ts +187 -0
- package/apps/agents-server/src/utils/externalChatRunner/createExternalAgentRepositoryFiles.ts +2 -2
- package/apps/agents-server/src/utils/serverManagement/createManagedServer/bootstrapManagedServer.ts +3 -1
- package/apps/agents-server/src/utils/serverManagement/createManagedServer/normalizeCreateServerInput.ts +6 -0
- package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerDefaultAgents.ts +7 -3
- package/apps/agents-server/src/utils/serverManagement/createManagedServer.ts +5 -0
- package/apps/agents-server/src/utils/userChat/listUserChats.ts +109 -0
- package/apps/agents-server/src/utils/userChat/triggerUserChatJobWorker.ts +54 -19
- package/apps/agents-server/src/utils/vpsConfiguration.ts +1 -1
- package/esm/index.es.js +9256 -8795
- package/esm/index.es.js.map +1 -1
- package/esm/scripts/run-agent-chat/executeAgentChatTurn.d.ts +28 -0
- package/esm/scripts/run-agent-chat/runAgentChat.d.ts +5 -0
- package/esm/scripts/run-agent-chat/runAgentExec.d.ts +11 -0
- package/esm/scripts/run-agent-messages/messages/createAgentRunnerSystemMessage.d.ts +10 -0
- package/esm/scripts/run-codex-prompts/common/resolveInlineOrFileText.d.ts +14 -0
- package/esm/src/_packages/node.index.d.ts +20 -0
- package/esm/src/_packages/types.index.d.ts +16 -0
- package/esm/src/book-3.0/BookNodeAgentSource.d.ts +38 -0
- package/esm/src/book-3.0/CliAgent.d.ts +68 -0
- package/esm/src/book-3.0/CliAgent.test.d.ts +1 -0
- package/esm/src/book-3.0/LiteAgent.d.ts +68 -0
- package/esm/src/book-3.0/LiteAgent.test.d.ts +1 -0
- package/esm/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.d.ts +12 -0
- package/esm/src/cli/cli-commands/agent/agentCliOptions.d.ts +29 -0
- package/esm/src/cli/cli-commands/agent/chat.d.ts +10 -0
- package/esm/src/cli/cli-commands/agent/exec.d.ts +10 -0
- package/esm/src/cli/cli-commands/agent/run.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/agent.d.ts +14 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +2 -2
- package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +9 -9
- package/esm/src/version.d.ts +1 -1
- package/package.json +2 -1
- package/src/_packages/node.index.ts +20 -0
- package/src/_packages/types.index.ts +16 -0
- package/src/book-3.0/BookNodeAgentSource.ts +135 -0
- package/src/book-3.0/CliAgent.ts +236 -0
- package/src/book-3.0/LiteAgent.ts +463 -0
- package/src/book-components/BookEditor/BookEditor.module.css +61 -0
- package/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.tsx +74 -0
- package/src/book-components/BookEditor/BookEditorActionbar.tsx +3 -3
- package/src/cli/cli-commands/agent/agentCliOptions.ts +63 -0
- package/src/cli/cli-commands/agent/chat.ts +54 -0
- package/src/cli/cli-commands/agent/exec.ts +60 -0
- package/src/cli/cli-commands/agent.ts +45 -0
- package/src/cli/cli-commands/agents-server/startAgentsServer.ts +2 -2
- package/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts +1 -1
- package/src/cli/cli-commands/common/promptRunnerCliOptions.ts +27 -23
- package/src/cli/promptbookCli.ts +2 -0
- package/src/other/templates/getTemplatesPipelineCollection.ts +809 -901
- package/src/version.ts +2 -2
- package/src/versions.txt +2 -0
- package/umd/index.umd.js +9255 -8794
- package/umd/index.umd.js.map +1 -1
- package/umd/scripts/run-agent-chat/executeAgentChatTurn.d.ts +28 -0
- package/umd/scripts/run-agent-chat/runAgentChat.d.ts +5 -0
- package/umd/scripts/run-agent-chat/runAgentExec.d.ts +11 -0
- package/umd/scripts/run-agent-messages/messages/createAgentRunnerSystemMessage.d.ts +10 -0
- package/umd/scripts/run-codex-prompts/common/resolveInlineOrFileText.d.ts +14 -0
- package/umd/src/_packages/node.index.d.ts +20 -0
- package/umd/src/_packages/types.index.d.ts +16 -0
- package/umd/src/book-3.0/BookNodeAgentSource.d.ts +38 -0
- package/umd/src/book-3.0/CliAgent.d.ts +68 -0
- package/umd/src/book-3.0/CliAgent.test.d.ts +1 -0
- package/umd/src/book-3.0/LiteAgent.d.ts +68 -0
- package/umd/src/book-3.0/LiteAgent.test.d.ts +1 -0
- package/umd/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.d.ts +12 -0
- package/umd/src/cli/cli-commands/agent/agentCliOptions.d.ts +29 -0
- package/umd/src/cli/cli-commands/agent/chat.d.ts +10 -0
- package/umd/src/cli/cli-commands/agent/exec.d.ts +10 -0
- package/umd/src/cli/cli-commands/agent/run.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/agent.d.ts +14 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +2 -2
- package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +9 -9
- package/umd/src/version.d.ts +1 -1
|
@@ -37,7 +37,7 @@ export async function readConfiguredCodeRunner(): Promise<ConfiguredCodeRunner>
|
|
|
37
37
|
>;
|
|
38
38
|
|
|
39
39
|
return {
|
|
40
|
-
agent: environmentByKey.
|
|
40
|
+
agent: environmentByKey.PTBK_HARNESS || process.env.PTBK_HARNESS || process.env.PTBK_AGENT || 'github-copilot',
|
|
41
41
|
model: environmentByKey.PTBK_MODEL || process.env.PTBK_MODEL || 'gpt-5.4',
|
|
42
42
|
thinkingLevel: environmentByKey.PTBK_THINKING_LEVEL || process.env.PTBK_THINKING_LEVEL || 'xhigh',
|
|
43
43
|
};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { BOOK_LANGUAGE_VERSION, PROMPTBOOK_ENGINE_VERSION } from '../../../../../src/version';
|
|
2
|
+
import {
|
|
3
|
+
createSentrySdkTags,
|
|
4
|
+
resolveSentrySdkCommitHash,
|
|
5
|
+
resolveSentrySdkEnvironment,
|
|
6
|
+
resolveSentrySdkRelease,
|
|
7
|
+
resolveSentrySdkRepositoryBranch,
|
|
8
|
+
} from './sentrySdkConfig';
|
|
9
|
+
import type { SentryStorePayload } from './sentryStore';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Shared deployment and runtime context added to every Agents Server Sentry event.
|
|
13
|
+
*/
|
|
14
|
+
type AgentsServerSentryContext = {
|
|
15
|
+
/**
|
|
16
|
+
* Sentry release identifier.
|
|
17
|
+
*/
|
|
18
|
+
readonly release: string;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Sentry environment identifier.
|
|
22
|
+
*/
|
|
23
|
+
readonly environment: string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Filterable Sentry tags.
|
|
27
|
+
*/
|
|
28
|
+
readonly tags: Record<string, string>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Full non-secret diagnostic context.
|
|
32
|
+
*/
|
|
33
|
+
readonly extra: Record<string, unknown>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Adds shared Agents Server release, environment, version, deployment, git, and runtime details.
|
|
38
|
+
*
|
|
39
|
+
* @param payload - Event-specific Sentry payload.
|
|
40
|
+
* @returns Payload enriched with common Agents Server diagnostics.
|
|
41
|
+
*
|
|
42
|
+
* @private internal helper for Agents Server Sentry reporting
|
|
43
|
+
*/
|
|
44
|
+
export function enrichSentryStorePayloadWithAgentsServerContext(payload: SentryStorePayload): SentryStorePayload {
|
|
45
|
+
const context = createAgentsServerSentryContext();
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
...payload,
|
|
49
|
+
release: context.release,
|
|
50
|
+
environment: context.environment,
|
|
51
|
+
tags: {
|
|
52
|
+
...context.tags,
|
|
53
|
+
...payload.tags,
|
|
54
|
+
},
|
|
55
|
+
extra: {
|
|
56
|
+
...context.extra,
|
|
57
|
+
...payload.extra,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Creates a current Agents Server diagnostic context matching the admin About page metadata.
|
|
64
|
+
*
|
|
65
|
+
* @returns Common Sentry context for one event.
|
|
66
|
+
*
|
|
67
|
+
* @private internal helper for Agents Server Sentry reporting
|
|
68
|
+
*/
|
|
69
|
+
export function createAgentsServerSentryContext(): AgentsServerSentryContext {
|
|
70
|
+
const deploymentEnvironment = resolveSentrySdkEnvironment();
|
|
71
|
+
const appPackageVersion = normalizeOptionalString(process.env.npm_package_version) ?? PROMPTBOOK_ENGINE_VERSION;
|
|
72
|
+
const commitHash = resolveSentrySdkCommitHash();
|
|
73
|
+
const repositoryBranch = resolveSentrySdkRepositoryBranch();
|
|
74
|
+
const memoryUsage = process.memoryUsage();
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
release: resolveSentrySdkRelease(),
|
|
78
|
+
environment: deploymentEnvironment,
|
|
79
|
+
tags: createSentrySdkTags(),
|
|
80
|
+
extra: {
|
|
81
|
+
agentsServer: {
|
|
82
|
+
versions: {
|
|
83
|
+
promptbookEngineVersion: PROMPTBOOK_ENGINE_VERSION,
|
|
84
|
+
bookLanguageVersion: BOOK_LANGUAGE_VERSION,
|
|
85
|
+
appPackageVersion,
|
|
86
|
+
appPackageName: normalizeOptionalString(process.env.npm_package_name),
|
|
87
|
+
nextBuildId: normalizeOptionalString(process.env.NEXT_BUILD_ID),
|
|
88
|
+
nextDistDirectory: normalizeOptionalString(process.env.NEXT_DIST_DIR),
|
|
89
|
+
npmLifecycleEvent: normalizeOptionalString(process.env.npm_lifecycle_event),
|
|
90
|
+
npmUserAgent: normalizeOptionalString(process.env.npm_config_user_agent),
|
|
91
|
+
},
|
|
92
|
+
deployment: {
|
|
93
|
+
environment: deploymentEnvironment,
|
|
94
|
+
vercelEnvironment: getFirstNonEmptyString(process.env.NEXT_PUBLIC_VERCEL_ENV, process.env.VERCEL_ENV),
|
|
95
|
+
targetEnvironment: getFirstNonEmptyString(
|
|
96
|
+
process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV,
|
|
97
|
+
process.env.VERCEL_TARGET_ENV,
|
|
98
|
+
),
|
|
99
|
+
siteUrl: normalizeOptionalString(process.env.NEXT_PUBLIC_SITE_URL),
|
|
100
|
+
vercelUrl: getFirstNonEmptyString(process.env.NEXT_PUBLIC_VERCEL_URL, process.env.VERCEL_URL),
|
|
101
|
+
vercelBranchUrl: normalizeOptionalString(process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL),
|
|
102
|
+
vercelProductionUrl: normalizeOptionalString(process.env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL),
|
|
103
|
+
vercelRegion: normalizeOptionalString(process.env.VERCEL_REGION),
|
|
104
|
+
repositoryRef: normalizeOptionalString(process.env.PROMPTBOOK_REPOSITORY_REF),
|
|
105
|
+
supabaseTablePrefix: normalizeOptionalString(process.env.SUPABASE_TABLE_PREFIX),
|
|
106
|
+
currentWorkingDirectory: process.cwd(),
|
|
107
|
+
},
|
|
108
|
+
git: {
|
|
109
|
+
provider: getFirstNonEmptyString(
|
|
110
|
+
process.env.NEXT_PUBLIC_VERCEL_GIT_PROVIDER,
|
|
111
|
+
process.env.VERCEL_GIT_PROVIDER,
|
|
112
|
+
),
|
|
113
|
+
repositoryOwner: getFirstNonEmptyString(
|
|
114
|
+
process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER,
|
|
115
|
+
process.env.VERCEL_GIT_REPO_OWNER,
|
|
116
|
+
),
|
|
117
|
+
repositorySlug: getFirstNonEmptyString(
|
|
118
|
+
process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG,
|
|
119
|
+
process.env.VERCEL_GIT_REPO_SLUG,
|
|
120
|
+
),
|
|
121
|
+
repositoryId: getFirstNonEmptyString(
|
|
122
|
+
process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_ID,
|
|
123
|
+
process.env.VERCEL_GIT_REPO_ID,
|
|
124
|
+
),
|
|
125
|
+
commitHash,
|
|
126
|
+
previousCommitHash: getFirstNonEmptyString(
|
|
127
|
+
process.env.NEXT_PUBLIC_VERCEL_GIT_PREVIOUS_SHA,
|
|
128
|
+
process.env.VERCEL_GIT_PREVIOUS_SHA,
|
|
129
|
+
),
|
|
130
|
+
branch: repositoryBranch,
|
|
131
|
+
commitMessage: getFirstNonEmptyString(
|
|
132
|
+
process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE,
|
|
133
|
+
process.env.VERCEL_GIT_COMMIT_MESSAGE,
|
|
134
|
+
),
|
|
135
|
+
authorName: getFirstNonEmptyString(
|
|
136
|
+
process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME,
|
|
137
|
+
process.env.VERCEL_GIT_COMMIT_AUTHOR_NAME,
|
|
138
|
+
),
|
|
139
|
+
authorLogin: getFirstNonEmptyString(
|
|
140
|
+
process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_LOGIN,
|
|
141
|
+
process.env.VERCEL_GIT_COMMIT_AUTHOR_LOGIN,
|
|
142
|
+
),
|
|
143
|
+
pullRequestId: getFirstNonEmptyString(
|
|
144
|
+
process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID,
|
|
145
|
+
process.env.VERCEL_GIT_PULL_REQUEST_ID,
|
|
146
|
+
),
|
|
147
|
+
},
|
|
148
|
+
runtime: {
|
|
149
|
+
nodeVersion: process.version,
|
|
150
|
+
nodeEnvironment: normalizeOptionalString(process.env.NODE_ENV),
|
|
151
|
+
nextRuntime: normalizeOptionalString(process.env.NEXT_RUNTIME),
|
|
152
|
+
processId: process.pid,
|
|
153
|
+
processUptimeSeconds: process.uptime(),
|
|
154
|
+
hostname: getFirstNonEmptyString(process.env.HOSTNAME, process.env.COMPUTERNAME),
|
|
155
|
+
platform: process.platform,
|
|
156
|
+
architecture: process.arch,
|
|
157
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC',
|
|
158
|
+
},
|
|
159
|
+
memory: {
|
|
160
|
+
rssBytes: memoryUsage.rss,
|
|
161
|
+
heapUsedBytes: memoryUsage.heapUsed,
|
|
162
|
+
heapTotalBytes: memoryUsage.heapTotal,
|
|
163
|
+
externalBytes: memoryUsage.external,
|
|
164
|
+
arrayBuffersBytes: memoryUsage.arrayBuffers ?? 0,
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Returns the first non-empty string from a list.
|
|
173
|
+
*
|
|
174
|
+
* @param values - Candidate values in priority order.
|
|
175
|
+
* @returns First trimmed string or null.
|
|
176
|
+
*/
|
|
177
|
+
function getFirstNonEmptyString(...values: Array<string | null | undefined>): string | null {
|
|
178
|
+
for (const value of values) {
|
|
179
|
+
const normalizedValue = normalizeOptionalString(value);
|
|
180
|
+
|
|
181
|
+
if (normalizedValue) {
|
|
182
|
+
return normalizedValue;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Trims one optional environment value.
|
|
191
|
+
*
|
|
192
|
+
* @param value - Raw optional value.
|
|
193
|
+
* @returns Trimmed string or null.
|
|
194
|
+
*/
|
|
195
|
+
function normalizeOptionalString(value: string | null | undefined): string | null {
|
|
196
|
+
if (typeof value !== 'string') {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const normalizedValue = value.trim();
|
|
201
|
+
|
|
202
|
+
return normalizedValue || null;
|
|
203
|
+
}
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import { inspect } from 'node:util';
|
|
2
|
+
import * as Sentry from '@sentry/nextjs';
|
|
3
|
+
import { DEFAULT_APPLICATION_ERROR_SERVER_NAME } from './applicationErrorHandling';
|
|
4
|
+
import { createAgentsServerSentryContext } from './agentsServerSentryContext';
|
|
5
|
+
import { enrichSentryStorePayloadWithAgentsServerContext } from './agentsServerSentryContext';
|
|
6
|
+
import {
|
|
7
|
+
createSentryTimestamp,
|
|
8
|
+
resolveOptionalSentryDsn,
|
|
9
|
+
sendSentryStorePayload,
|
|
10
|
+
type SentryStorePayload,
|
|
11
|
+
} from './sentryStore';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Logger name visible in Sentry events for server-side `console.error` calls.
|
|
15
|
+
*/
|
|
16
|
+
const SENTRY_SERVER_ERROR_LOGGER = 'agents-server.server-error';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Marker used to keep the global console bridge idempotent across hot reloads.
|
|
20
|
+
*/
|
|
21
|
+
const SERVER_ERROR_SENTRY_LOGGING_STATE_SYMBOL = Symbol.for('promptbook.agents-server.serverErrorSentryLoggingState');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Default label used when `console.error` is called without a string or error object.
|
|
25
|
+
*/
|
|
26
|
+
const DEFAULT_SERVER_ERROR_MESSAGE = 'Agents Server console.error';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Keys that commonly hold nested error payloads inside structured logs.
|
|
30
|
+
*/
|
|
31
|
+
const NESTED_ERROR_PROPERTY_NAMES = ['error', 'cause', 'reason'] as const;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Maximum traversal depth when looking for nested error-like objects.
|
|
35
|
+
*/
|
|
36
|
+
const MAX_ERROR_SEARCH_DEPTH = 2;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Shared global state for the server-side console bridge.
|
|
40
|
+
*/
|
|
41
|
+
type ServerErrorSentryLoggingState = {
|
|
42
|
+
/**
|
|
43
|
+
* Original console implementation preserved before patching.
|
|
44
|
+
*/
|
|
45
|
+
originalConsoleError: typeof console.error | null;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* True once the bridge has been installed.
|
|
49
|
+
*/
|
|
50
|
+
isRegistered: boolean;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Normalized error details extracted from logged values.
|
|
55
|
+
*/
|
|
56
|
+
type LoggedErrorInfo = {
|
|
57
|
+
/**
|
|
58
|
+
* Best available error type.
|
|
59
|
+
*/
|
|
60
|
+
name: string;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Best available error message.
|
|
64
|
+
*/
|
|
65
|
+
message: string;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Optional stack trace when present.
|
|
69
|
+
*/
|
|
70
|
+
stack?: string;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Registers one server-side `console.error` bridge that forwards logs to Sentry.
|
|
75
|
+
*/
|
|
76
|
+
export function registerServerErrorSentryLogging(): void {
|
|
77
|
+
const loggingState = getServerErrorSentryLoggingState();
|
|
78
|
+
|
|
79
|
+
if (loggingState.isRegistered) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
loggingState.originalConsoleError = console.error.bind(console);
|
|
84
|
+
console.error = (...consoleArguments: unknown[]): void => {
|
|
85
|
+
loggingState.originalConsoleError?.(...consoleArguments);
|
|
86
|
+
|
|
87
|
+
const sentryPayload = createServerErrorSentryStorePayload(consoleArguments);
|
|
88
|
+
|
|
89
|
+
if (captureServerErrorWithSentrySdk(sentryPayload)) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const sentryDsn = resolveOptionalSentryDsn();
|
|
94
|
+
if (!sentryDsn) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Never log reporting failures through `console.error`, otherwise a broken Sentry configuration would recurse.
|
|
99
|
+
void sendSentryStorePayload(sentryPayload, sentryDsn).catch(() => undefined);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
loggingState.isRegistered = true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Restores the original `console.error` implementation after tests patch it.
|
|
107
|
+
*
|
|
108
|
+
* @private test helper for server-side Sentry console forwarding
|
|
109
|
+
*/
|
|
110
|
+
export function $resetServerErrorSentryLoggingForTests(): void {
|
|
111
|
+
const loggingState = getServerErrorSentryLoggingState();
|
|
112
|
+
|
|
113
|
+
if (loggingState.originalConsoleError) {
|
|
114
|
+
console.error = loggingState.originalConsoleError;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
loggingState.originalConsoleError = null;
|
|
118
|
+
loggingState.isRegistered = false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Returns the shared mutable global state used by the bridge.
|
|
123
|
+
*
|
|
124
|
+
* @returns Shared state object reused across reloads.
|
|
125
|
+
*/
|
|
126
|
+
function getServerErrorSentryLoggingState(): ServerErrorSentryLoggingState {
|
|
127
|
+
const globalObject = globalThis as typeof globalThis & Record<PropertyKey, unknown>;
|
|
128
|
+
|
|
129
|
+
const existingLoggingState = globalObject[
|
|
130
|
+
SERVER_ERROR_SENTRY_LOGGING_STATE_SYMBOL
|
|
131
|
+
] as ServerErrorSentryLoggingState | undefined;
|
|
132
|
+
if (existingLoggingState) {
|
|
133
|
+
return existingLoggingState;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const newLoggingState: ServerErrorSentryLoggingState = {
|
|
137
|
+
originalConsoleError: null,
|
|
138
|
+
isRegistered: false,
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
globalObject[SERVER_ERROR_SENTRY_LOGGING_STATE_SYMBOL] = newLoggingState;
|
|
142
|
+
return newLoggingState;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Creates one Sentry store payload from a `console.error` call.
|
|
147
|
+
*
|
|
148
|
+
* @param consoleArguments - Original `console.error` arguments.
|
|
149
|
+
* @returns Structured Sentry event payload.
|
|
150
|
+
*/
|
|
151
|
+
function createServerErrorSentryStorePayload(consoleArguments: readonly unknown[]): SentryStorePayload {
|
|
152
|
+
const loggedError = findLoggedErrorInfo(consoleArguments);
|
|
153
|
+
|
|
154
|
+
return enrichSentryStorePayloadWithAgentsServerContext({
|
|
155
|
+
platform: 'javascript',
|
|
156
|
+
level: 'error',
|
|
157
|
+
logger: SENTRY_SERVER_ERROR_LOGGER,
|
|
158
|
+
timestamp: createSentryTimestamp(),
|
|
159
|
+
message: createServerErrorMessage(consoleArguments, loggedError),
|
|
160
|
+
server_name: process.env.NEXT_PUBLIC_SERVER_NAME ?? DEFAULT_APPLICATION_ERROR_SERVER_NAME,
|
|
161
|
+
tags: {
|
|
162
|
+
source: 'agents-server.console-error',
|
|
163
|
+
},
|
|
164
|
+
exception: loggedError
|
|
165
|
+
? {
|
|
166
|
+
values: [
|
|
167
|
+
{
|
|
168
|
+
type: loggedError.name,
|
|
169
|
+
value: loggedError.message,
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
}
|
|
173
|
+
: undefined,
|
|
174
|
+
extra: {
|
|
175
|
+
consoleArguments: consoleArguments.map(serializeConsoleArgument),
|
|
176
|
+
errorStack: loggedError?.stack ?? null,
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Captures a server-side console error through the official Sentry SDK when it is initialized.
|
|
183
|
+
*
|
|
184
|
+
* @param sentryPayload - Structured event payload created from the original `console.error` call.
|
|
185
|
+
* @returns True when the SDK accepted the event.
|
|
186
|
+
*/
|
|
187
|
+
function captureServerErrorWithSentrySdk(sentryPayload: SentryStorePayload): boolean {
|
|
188
|
+
if (!Sentry.getClient()) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const agentsServerContext = createAgentsServerSentryContext();
|
|
193
|
+
|
|
194
|
+
Sentry.captureException(createServerErrorSdkException(sentryPayload), {
|
|
195
|
+
level: sentryPayload.level,
|
|
196
|
+
tags: {
|
|
197
|
+
...agentsServerContext.tags,
|
|
198
|
+
...sentryPayload.tags,
|
|
199
|
+
},
|
|
200
|
+
extra: {
|
|
201
|
+
...agentsServerContext.extra,
|
|
202
|
+
...sentryPayload.extra,
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Rebuilds an `Error` object from the structured Sentry payload so the SDK can parse stack frames.
|
|
211
|
+
*
|
|
212
|
+
* @param sentryPayload - Structured event payload created from the original `console.error` call.
|
|
213
|
+
* @returns Error object with the original stack when one was logged.
|
|
214
|
+
*/
|
|
215
|
+
function createServerErrorSdkException(sentryPayload: SentryStorePayload): Error {
|
|
216
|
+
const error = new Error(sentryPayload.message);
|
|
217
|
+
const exceptionValue = sentryPayload.exception?.values.at(0);
|
|
218
|
+
const errorStack = sentryPayload.extra?.errorStack;
|
|
219
|
+
|
|
220
|
+
error.name = exceptionValue?.type ?? 'Error';
|
|
221
|
+
|
|
222
|
+
if (typeof errorStack === 'string' && errorStack.trim()) {
|
|
223
|
+
error.stack = errorStack;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return error;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Creates one stable event message from a `console.error` call.
|
|
231
|
+
*
|
|
232
|
+
* @param consoleArguments - Original `console.error` arguments.
|
|
233
|
+
* @param loggedError - First extracted error-like payload when available.
|
|
234
|
+
* @returns Event message suitable for Sentry grouping and scanning.
|
|
235
|
+
*/
|
|
236
|
+
function createServerErrorMessage(
|
|
237
|
+
consoleArguments: readonly unknown[],
|
|
238
|
+
loggedError: LoggedErrorInfo | null,
|
|
239
|
+
): string {
|
|
240
|
+
const stringMessage = consoleArguments
|
|
241
|
+
.filter((consoleArgument): consoleArgument is string => typeof consoleArgument === 'string')
|
|
242
|
+
.map((consoleArgument) => consoleArgument.trim())
|
|
243
|
+
.filter(Boolean)
|
|
244
|
+
.join(' ')
|
|
245
|
+
.trim();
|
|
246
|
+
|
|
247
|
+
if (stringMessage && loggedError?.message && !stringMessage.includes(loggedError.message)) {
|
|
248
|
+
return `${stringMessage} ${loggedError.message}`.trim();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (stringMessage) {
|
|
252
|
+
return stringMessage;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (loggedError?.message) {
|
|
256
|
+
return loggedError.message;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const firstConsoleArgument = consoleArguments.at(0);
|
|
260
|
+
if (firstConsoleArgument !== undefined) {
|
|
261
|
+
return serializeConsoleArgument(firstConsoleArgument);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return DEFAULT_SERVER_ERROR_MESSAGE;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Extracts the first meaningful error-like payload from structured console arguments.
|
|
269
|
+
*
|
|
270
|
+
* @param consoleArguments - Original `console.error` arguments.
|
|
271
|
+
* @returns Normalized logged error payload or `null`.
|
|
272
|
+
*/
|
|
273
|
+
function findLoggedErrorInfo(consoleArguments: readonly unknown[]): LoggedErrorInfo | null {
|
|
274
|
+
const visitedObjects = new WeakSet<object>();
|
|
275
|
+
|
|
276
|
+
for (const consoleArgument of consoleArguments) {
|
|
277
|
+
const loggedError = findLoggedErrorInfoInValue(consoleArgument, visitedObjects, 0);
|
|
278
|
+
if (loggedError) {
|
|
279
|
+
return loggedError;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Recursively searches one logged value for error-like payloads.
|
|
288
|
+
*
|
|
289
|
+
* @param value - Current logged value.
|
|
290
|
+
* @param visitedObjects - Cycle guard for nested objects.
|
|
291
|
+
* @param depth - Current recursion depth.
|
|
292
|
+
* @returns Normalized logged error payload or `null`.
|
|
293
|
+
*/
|
|
294
|
+
function findLoggedErrorInfoInValue(
|
|
295
|
+
value: unknown,
|
|
296
|
+
visitedObjects: WeakSet<object>,
|
|
297
|
+
depth: number,
|
|
298
|
+
): LoggedErrorInfo | null {
|
|
299
|
+
if (value instanceof Error) {
|
|
300
|
+
return {
|
|
301
|
+
name: value.name || 'Error',
|
|
302
|
+
message: value.message || DEFAULT_SERVER_ERROR_MESSAGE,
|
|
303
|
+
stack: value.stack,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (!value || typeof value !== 'object') {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (visitedObjects.has(value)) {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
visitedObjects.add(value);
|
|
315
|
+
|
|
316
|
+
const normalizedErrorInfo = normalizeLoggedErrorInfo(value as Record<string, unknown>);
|
|
317
|
+
if (normalizedErrorInfo) {
|
|
318
|
+
return normalizedErrorInfo;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (depth >= MAX_ERROR_SEARCH_DEPTH) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
for (const propertyName of NESTED_ERROR_PROPERTY_NAMES) {
|
|
326
|
+
if (!(propertyName in value)) {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const nestedErrorInfo = findLoggedErrorInfoInValue(
|
|
331
|
+
(value as Record<string, unknown>)[propertyName],
|
|
332
|
+
visitedObjects,
|
|
333
|
+
depth + 1,
|
|
334
|
+
);
|
|
335
|
+
if (nestedErrorInfo) {
|
|
336
|
+
return nestedErrorInfo;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Converts one plain object into normalized error details when it looks like an error.
|
|
345
|
+
*
|
|
346
|
+
* @param value - Plain logged object.
|
|
347
|
+
* @returns Normalized logged error payload or `null`.
|
|
348
|
+
*/
|
|
349
|
+
function normalizeLoggedErrorInfo(value: Record<string, unknown>): LoggedErrorInfo | null {
|
|
350
|
+
const rawErrorName = typeof value.name === 'string' ? value.name.trim() : '';
|
|
351
|
+
const rawErrorMessage = typeof value.message === 'string' ? value.message.trim() : '';
|
|
352
|
+
const rawErrorStack = typeof value.stack === 'string' ? value.stack.trim() : '';
|
|
353
|
+
|
|
354
|
+
if (!rawErrorName && !rawErrorStack) {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
name: rawErrorName || 'Error',
|
|
360
|
+
message: rawErrorMessage || DEFAULT_SERVER_ERROR_MESSAGE,
|
|
361
|
+
stack: rawErrorStack || undefined,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Serializes one logged console value into a stable string representation.
|
|
367
|
+
*
|
|
368
|
+
* @param value - Original console argument.
|
|
369
|
+
* @returns Stable string suitable for Sentry `extra`.
|
|
370
|
+
*/
|
|
371
|
+
function serializeConsoleArgument(value: unknown): string {
|
|
372
|
+
if (typeof value === 'string') {
|
|
373
|
+
return value;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return inspect(value, {
|
|
377
|
+
depth: 6,
|
|
378
|
+
breakLength: 120,
|
|
379
|
+
maxArrayLength: 50,
|
|
380
|
+
});
|
|
381
|
+
}
|