@promptbook/cli 0.112.0-112 → 0.112.0-114

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 (94) 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/agents/[agentName]/api/user-chats/[chatId]/stream/route.ts +85 -56
  5. package/apps/agents-server/src/app/agents/[agentName]/chat/useAgentChatHistorySyncEffects.ts +7 -13
  6. package/apps/agents-server/src/app/api/admin/code-runners/route.ts +1 -1
  7. package/apps/agents-server/src/app/api/internal/user-chat-jobs/run/route.ts +1 -1
  8. package/apps/agents-server/src/database/migrations/2026-06-1300-user-chat-active-read-indexes.sql +7 -0
  9. package/apps/agents-server/src/instrumentation-client.ts +28 -0
  10. package/apps/agents-server/src/instrumentation.ts +16 -0
  11. package/apps/agents-server/src/sentry.edge.config.ts +18 -0
  12. package/apps/agents-server/src/sentry.server.config.ts +19 -0
  13. package/apps/agents-server/src/utils/agentRouting/resolveAgentRouteTarget.ts +38 -0
  14. package/apps/agents-server/src/utils/codeRunnerConfiguration.ts +1 -1
  15. package/apps/agents-server/src/utils/errorReporting/agentsServerSentryContext.ts +203 -0
  16. package/apps/agents-server/src/utils/errorReporting/registerServerErrorSentryLogging.ts +59 -9
  17. package/apps/agents-server/src/utils/errorReporting/sendApplicationErrorReportToSentry.ts +39 -3
  18. package/apps/agents-server/src/utils/errorReporting/sentrySdkConfig.ts +237 -0
  19. package/apps/agents-server/src/utils/errorReporting/sentryStore.ts +10 -0
  20. package/apps/agents-server/src/utils/externalChatRunner/createExternalAgentRepositoryFiles.ts +2 -2
  21. package/apps/agents-server/src/utils/userChat/createImmediateUserChatAnswerModelRequirements.ts +15 -12
  22. package/apps/agents-server/src/utils/userChat/createUserChatDetailPayload.ts +33 -18
  23. package/apps/agents-server/src/utils/userChat/hasPotentiallyPendingAssistantMessages.ts +26 -0
  24. package/apps/agents-server/src/utils/userChat/runImmediateUserChatAnswer.ts +1 -1
  25. package/apps/agents-server/src/utils/userChat/triggerUserChatJobWorker.ts +54 -19
  26. package/apps/agents-server/src/utils/vpsConfiguration.ts +1 -1
  27. package/esm/index.es.js +9285 -8807
  28. package/esm/index.es.js.map +1 -1
  29. package/esm/scripts/run-agent-chat/executeAgentChatTurn.d.ts +28 -0
  30. package/esm/scripts/run-agent-chat/runAgentChat.d.ts +5 -0
  31. package/esm/scripts/run-agent-chat/runAgentExec.d.ts +11 -0
  32. package/esm/scripts/run-agent-messages/messages/createAgentRunnerSystemMessage.d.ts +10 -0
  33. package/esm/scripts/run-codex-prompts/common/resolveInlineOrFileText.d.ts +14 -0
  34. package/esm/scripts/run-codex-prompts/common/runGoScript/printLiveScriptChunk.d.ts +4 -0
  35. package/esm/src/_packages/node.index.d.ts +20 -0
  36. package/esm/src/_packages/types.index.d.ts +16 -0
  37. package/esm/src/book-3.0/BookNodeAgentSource.d.ts +38 -0
  38. package/esm/src/book-3.0/CliAgent.d.ts +68 -0
  39. package/esm/src/book-3.0/LiteAgent.d.ts +68 -0
  40. package/esm/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.d.ts +12 -0
  41. package/esm/src/cli/cli-commands/agent/agentCliOptions.d.ts +38 -0
  42. package/esm/src/cli/cli-commands/agent/chat.d.ts +10 -0
  43. package/esm/src/cli/cli-commands/agent/exec.d.ts +10 -0
  44. package/esm/src/cli/cli-commands/agent/run.test.d.ts +1 -0
  45. package/esm/src/cli/cli-commands/agent.d.ts +14 -0
  46. package/esm/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +3 -4
  47. package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +9 -9
  48. package/esm/src/version.d.ts +1 -1
  49. package/package.json +2 -1
  50. package/src/_packages/node.index.ts +20 -0
  51. package/src/_packages/types.index.ts +16 -0
  52. package/src/book-3.0/BookNodeAgentSource.ts +135 -0
  53. package/src/book-3.0/CliAgent.ts +236 -0
  54. package/src/book-3.0/LiteAgent.ts +468 -0
  55. package/src/book-components/BookEditor/BookEditor.module.css +61 -0
  56. package/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.tsx +74 -0
  57. package/src/book-components/BookEditor/BookEditorActionbar.tsx +3 -3
  58. package/src/cli/cli-commands/agent/agentCliOptions.ts +92 -0
  59. package/src/cli/cli-commands/agent/chat.ts +54 -0
  60. package/src/cli/cli-commands/agent/exec.ts +60 -0
  61. package/src/cli/cli-commands/agent.ts +44 -0
  62. package/src/cli/cli-commands/agents-server/startAgentsServer.ts +3 -19
  63. package/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts +1 -1
  64. package/src/cli/cli-commands/common/promptRunnerCliOptions.ts +27 -23
  65. package/src/cli/promptbookCli.ts +2 -0
  66. package/src/other/templates/getTemplatesPipelineCollection.ts +747 -771
  67. package/src/version.ts +2 -2
  68. package/src/versions.txt +2 -0
  69. package/umd/index.umd.js +9285 -8807
  70. package/umd/index.umd.js.map +1 -1
  71. package/umd/scripts/run-agent-chat/executeAgentChatTurn.d.ts +28 -0
  72. package/umd/scripts/run-agent-chat/runAgentChat.d.ts +5 -0
  73. package/umd/scripts/run-agent-chat/runAgentExec.d.ts +11 -0
  74. package/umd/scripts/run-agent-messages/messages/createAgentRunnerSystemMessage.d.ts +10 -0
  75. package/umd/scripts/run-codex-prompts/common/resolveInlineOrFileText.d.ts +14 -0
  76. package/umd/scripts/run-codex-prompts/common/runGoScript/printLiveScriptChunk.d.ts +4 -0
  77. package/umd/src/_packages/node.index.d.ts +20 -0
  78. package/umd/src/_packages/types.index.d.ts +16 -0
  79. package/umd/src/book-3.0/BookNodeAgentSource.d.ts +38 -0
  80. package/umd/src/book-3.0/CliAgent.d.ts +68 -0
  81. package/umd/src/book-3.0/CliAgent.test.d.ts +1 -0
  82. package/umd/src/book-3.0/LiteAgent.d.ts +68 -0
  83. package/umd/src/book-3.0/LiteAgent.test.d.ts +1 -0
  84. package/umd/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.d.ts +12 -0
  85. package/umd/src/cli/cli-commands/agent/agentCliOptions.d.ts +38 -0
  86. package/umd/src/cli/cli-commands/agent/chat.d.ts +10 -0
  87. package/umd/src/cli/cli-commands/agent/exec.d.ts +10 -0
  88. package/umd/src/cli/cli-commands/agent/run.test.d.ts +1 -0
  89. package/umd/src/cli/cli-commands/agent.d.ts +14 -0
  90. package/umd/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +3 -4
  91. package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +9 -9
  92. package/umd/src/version.d.ts +1 -1
  93. /package/esm/src/{cli/cli-commands/agents-server/startAgentsServer.test.d.ts → book-3.0/CliAgent.test.d.ts} +0 -0
  94. /package/{umd/src/cli/cli-commands/agents-server/startAgentsServer.test.d.ts → esm/src/book-3.0/LiteAgent.test.d.ts} +0 -0
package/README.md CHANGED
@@ -561,11 +561,11 @@ npx ts-node ./src/cli/test/ptbk.ts coder generate-boilerplates --template prompt
561
561
 
562
562
  npx ts-node ./src/cli/test/ptbk.ts coder generate-boilerplates --template prompts/templates/agents-server.md
563
563
 
564
- npx ts-node ./src/cli/test/ptbk.ts coder run --agent github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md
564
+ npx ts-node ./src/cli/test/ptbk.ts coder run --harness github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md
565
565
 
566
- npx ts-node ./src/cli/test/ptbk.ts coder run --agent github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md --auto-push
566
+ npx ts-node ./src/cli/test/ptbk.ts coder run --harness github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md --auto-push
567
567
 
568
- npx ts-node ./src/cli/test/ptbk.ts coder run --agent github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md --ignore-git-changes --no-wait
568
+ npx ts-node ./src/cli/test/ptbk.ts coder run --harness github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md --ignore-git-changes --no-wait
569
569
 
570
570
  npx ts-node ./src/cli/test/ptbk.ts coder find-refactor-candidates
571
571
 
@@ -587,11 +587,11 @@ ptbk coder generate-boilerplates
587
587
 
588
588
  ptbk coder generate-boilerplates --template prompts/templates/common.md
589
589
 
590
- ptbk coder run --agent github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md --test npm run test
590
+ ptbk coder run --harness github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md --test npm run test
591
591
 
592
- ptbk coder run --agent github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md --auto-push
592
+ ptbk coder run --harness github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md --auto-push
593
593
 
594
- ptbk coder run --agent github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md --test npm run test --ignore-git-changes --no-wait
594
+ ptbk coder run --harness github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md --test npm run test --ignore-git-changes --no-wait
595
595
 
596
596
  ptbk coder find-refactor-candidates
597
597
 
@@ -616,7 +616,7 @@ ptbk coder verify
616
616
 
617
617
  | Flag | Purpose |
618
618
  | -------------------------- | -------------------------------------------------------------------------------------------------- |
619
- | `--agent <name>` | Selects the coding backend. |
619
+ | `--harness <name>` | Selects the coding harness. |
620
620
  | `--model <model>` | Chooses the runner model; required for `openai-codex` and `gemini`, optional for `github-copilot`. |
621
621
  | `--context <text-or-file>` | Appends extra instructions inline or from a file like `AGENTS.md`. |
622
622
  | `--test <command>` | Runs a verification command after each prompt attempt and feeds failing output back for retries. |
@@ -9,7 +9,7 @@ The installed CLI can initialize a local Agents Server project before it starts
9
9
  ```bash
10
10
  npm install ptbk
11
11
  ptbk agents-server init
12
- ptbk agents-server start --agent github-copilot --model gpt-5.4 --thinking-level xhigh
12
+ ptbk agents-server start --harness github-copilot --model gpt-5.4 --thinking-level xhigh
13
13
  ```
14
14
 
15
15
  `ptbk agents-server init` adds missing placeholders to `.env` and local runtime exclusions to `.gitignore` without deleting existing configuration. Use `PTBK_AGENTS_SERVER_DATABASE=supabase` for a Supabase-backed server or `PTBK_AGENTS_SERVER_DATABASE=sqlite` for a standalone local database in `.promptbook`. When using Supabase, fill the initialized values from your project before starting the server. The Supabase project URL and API keys are available in the [Supabase project API settings](https://supabase.com/docs/guides/api/api-keys), and the PostgreSQL connection string is available from the [Supabase database connection guide](https://supabase.com/docs/guides/database/connecting-to-postgres).
@@ -1,6 +1,8 @@
1
+ import { withSentryConfig } from '@sentry/nextjs';
1
2
  import type { NextConfig } from 'next';
2
3
  import { readdirSync } from 'fs';
3
4
  import path from 'path';
5
+ import { SENTRY_ORGANIZATION, SENTRY_PROJECT } from './src/utils/errorReporting/sentrySdkConfig';
4
6
 
5
7
  /**
6
8
  * Next.js build output directory.
@@ -20,6 +22,11 @@ const agentsServerNodeModulesPath = process.env.PTBK_AGENTS_SERVER_NODE_MODULES_
20
22
  */
21
23
  const isNextValidationIgnored = process.env.PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION === 'true';
22
24
 
25
+ /**
26
+ * Whether Sentry source maps can be uploaded during production builds.
27
+ */
28
+ const IS_SENTRY_SOURCE_MAP_UPLOAD_ENABLED = Boolean(process.env.SENTRY_AUTH_TOKEN);
29
+
23
30
  /**
24
31
  * Exact aliases for local generated Promptbook package entrypoints.
25
32
  */
@@ -122,7 +129,19 @@ const nextConfig: NextConfig = {
122
129
  },
123
130
  };
124
131
 
125
- export default nextConfig;
132
+ export default withSentryConfig(nextConfig, {
133
+ org: SENTRY_ORGANIZATION,
134
+ project: SENTRY_PROJECT,
135
+ silent: true,
136
+ sourcemaps: {
137
+ disable: !IS_SENTRY_SOURCE_MAP_UPLOAD_ENABLED,
138
+ },
139
+ webpack: {
140
+ treeshake: {
141
+ removeDebugLogging: true,
142
+ },
143
+ },
144
+ });
126
145
 
127
146
  /**
128
147
  * Creates webpack/Turbopack aliases from imports like `@promptbook-local/core` to generated local sources.
@@ -1,7 +1,13 @@
1
1
  import { CHAT_STREAM_KEEP_ALIVE_INTERVAL_MS } from '@/src/constants/streaming';
2
2
  import { isPrivateModeEnabledFromRequest } from '@/src/utils/privateMode';
3
- import { createUserChatDetailPayload, getUserChat, isFrozenUserChatSource } from '@/src/utils/userChat';
4
- import type { ChatMessage } from '@promptbook-local/types';
3
+ import {
4
+ createUserChatDetailPayload,
5
+ getUserChat,
6
+ isFrozenUserChatSource,
7
+ listUserChatJobs,
8
+ } from '@/src/utils/userChat';
9
+ import { hasPotentiallyPendingAssistantMessages } from '@/src/utils/userChat/hasPotentiallyPendingAssistantMessages';
10
+ import { listUserChatTimeouts } from '@/src/utils/userChatTimeout/userChatTimeoutStore';
5
11
  import { NextResponse } from 'next/server';
6
12
  import { resolveUserChatScope } from '../../resolveUserChatScope';
7
13
 
@@ -136,17 +142,17 @@ export async function GET(request: Request, { params }: { params: Promise<{ agen
136
142
  return false;
137
143
  }
138
144
 
139
- const payload = await createUserChatDetailPayload(currentChat);
140
- const nextSignature = createUserChatDetailSignature(payload);
145
+ const livePollState = await loadLiveUserChatPollingState(currentChat);
141
146
 
142
- if (nextSignature !== lastSnapshotSignature) {
143
- lastSnapshotSignature = nextSignature;
147
+ if (livePollState.signature !== lastSnapshotSignature) {
148
+ const payload = await createUserChatDetailPayload(currentChat);
149
+ lastSnapshotSignature = createUserChatPollingSignatureFromDetailPayload(payload);
144
150
  if (!enqueueFrame({ type: 'snapshot', payload })) {
145
151
  return false;
146
152
  }
147
153
  }
148
154
 
149
- return !isFrozenUserChatSource(payload.chat.source) && payload.activeJobs.length > 0;
155
+ return !isFrozenUserChatSource(currentChat.source) && livePollState.hasActiveJobs;
150
156
  };
151
157
 
152
158
  /**
@@ -211,69 +217,92 @@ export async function GET(request: Request, { params }: { params: Promise<{ agen
211
217
  }
212
218
 
213
219
  /**
214
- * Builds a stable signature for the user-visible parts of a canonical chat snapshot.
220
+ * Minimal live poll state used to avoid rebuilding the full chat payload when nothing visible changed.
215
221
  */
216
- function createUserChatDetailSignature(payload: Awaited<ReturnType<typeof createUserChatDetailPayload>>): string {
217
- return JSON.stringify({
218
- chatId: payload.chat.id,
219
- updatedAt: payload.chat.updatedAt,
220
- draftMessage: payload.draftMessage || '',
221
- messages: payload.messages.map(createUserChatMessageSignature),
222
- activeJobs: payload.activeJobs.map((job) => ({
223
- id: job.id,
224
- status: job.status,
225
- cancelRequestedAt: job.cancelRequestedAt,
226
- })),
227
- activeTimeouts: payload.activeTimeouts.map((timeout) => ({
228
- id: timeout.id,
229
- status: timeout.status,
230
- dueAt: timeout.dueAt,
231
- cancelRequestedAt: timeout.cancelRequestedAt,
232
- })),
233
- });
234
- }
222
+ type LiveUserChatPollState = {
223
+ signature: string;
224
+ hasActiveJobs: boolean;
225
+ };
235
226
 
236
227
  /**
237
- * Builds one compact stable signature for a user-visible chat message.
228
+ * Loads the lightweight state needed to decide whether the active chat payload changed.
229
+ *
230
+ * The expensive detail payload does job reconciliation, local-runner synchronization, and full
231
+ * transcript serialization. Polling first with the persisted timestamps + active resource state
232
+ * keeps long-lived chat streams cheap when the conversation is idle.
233
+ *
234
+ * @param chat - Current scoped chat record.
235
+ * @returns Poll signature plus whether the chat should keep using the active-job cadence.
238
236
  */
239
- function createUserChatMessageSignature(
240
- message: Awaited<ReturnType<typeof createUserChatDetailPayload>>['messages'][number],
241
- ): Record<string, unknown> {
237
+ async function loadLiveUserChatPollingState(chat: NonNullable<Awaited<ReturnType<typeof getUserChat>>>): Promise<LiveUserChatPollState> {
238
+ const shouldInspectActiveJobs = hasPotentiallyPendingAssistantMessages(chat.messages);
239
+ const [activeJobs, activeTimeouts] = await Promise.all([
240
+ shouldInspectActiveJobs
241
+ ? listUserChatJobs({
242
+ userId: chat.userId,
243
+ agentPermanentId: chat.agentPermanentId,
244
+ chatId: chat.id,
245
+ onlyActive: true,
246
+ })
247
+ : Promise.resolve([]),
248
+ listUserChatTimeouts({
249
+ userId: chat.userId,
250
+ agentPermanentId: chat.agentPermanentId,
251
+ chatId: chat.id,
252
+ onlyActive: true,
253
+ }),
254
+ ]);
255
+
242
256
  return {
243
- id: message.id ?? null,
244
- sender: message.sender,
245
- isComplete: message.isComplete,
246
- lifecycleState: message.lifecycleState ?? null,
247
- lifecycleError: message.lifecycleError ?? null,
248
- contentLength: message.content.length,
249
- contentHash: createStableTextDigest(message.content),
250
- replyingTo: message.replyingTo ? JSON.stringify(message.replyingTo) : null,
251
- progressCard: message.progressCard ? JSON.stringify(message.progressCard) : null,
252
- ongoingToolCalls: createToolCallsSignature(message.ongoingToolCalls),
253
- completedToolCalls: createToolCallsSignature(message.completedToolCalls),
254
- toolCalls: createToolCallsSignature(message.toolCalls),
257
+ signature: createUserChatPollingSignature({
258
+ chatUpdatedAt: chat.updatedAt,
259
+ draftMessage: chat.draftMessage,
260
+ activeJobs,
261
+ activeTimeouts,
262
+ }),
263
+ hasActiveJobs: activeJobs.length > 0,
255
264
  };
256
265
  }
257
266
 
258
267
  /**
259
- * Creates one compact stable digest for message text without pulling in heavier hashing helpers.
268
+ * Builds the polling signature for a fully hydrated detail payload.
260
269
  */
261
- function createStableTextDigest(value: string): string {
262
- let hash = 2_166_136_261;
263
-
264
- for (let index = 0; index < value.length; index++) {
265
- hash ^= value.charCodeAt(index);
266
- hash = Math.imul(hash, 16_777_619);
267
- }
268
-
269
- return (hash >>> 0).toString(16);
270
+ function createUserChatPollingSignatureFromDetailPayload(
271
+ payload: Awaited<ReturnType<typeof createUserChatDetailPayload>>,
272
+ ): string {
273
+ return createUserChatPollingSignature({
274
+ chatUpdatedAt: payload.chat.updatedAt,
275
+ draftMessage: payload.draftMessage,
276
+ activeJobs: payload.activeJobs,
277
+ activeTimeouts: payload.activeTimeouts,
278
+ });
270
279
  }
271
280
 
272
281
  /**
273
- * Serializes optional tool-call arrays for snapshot signature comparisons.
282
+ * Builds a stable signature for the user-visible state that changes stream snapshots.
274
283
  */
275
- function createToolCallsSignature(toolCalls: ChatMessage['toolCalls']): string | null {
276
- return toolCalls && toolCalls.length > 0 ? JSON.stringify(toolCalls) : null;
284
+ function createUserChatPollingSignature(options: {
285
+ chatUpdatedAt: string;
286
+ draftMessage: string | null;
287
+ activeJobs: ReadonlyArray<Awaited<ReturnType<typeof createUserChatDetailPayload>>['activeJobs'][number]>;
288
+ activeTimeouts: ReadonlyArray<Awaited<ReturnType<typeof createUserChatDetailPayload>>['activeTimeouts'][number]>;
289
+ }): string {
290
+ return JSON.stringify({
291
+ updatedAt: options.chatUpdatedAt,
292
+ draftMessage: options.draftMessage || '',
293
+ activeJobs: options.activeJobs.map((job) => ({
294
+ id: job.id,
295
+ status: job.status,
296
+ cancelRequestedAt: job.cancelRequestedAt,
297
+ })),
298
+ activeTimeouts: options.activeTimeouts.map((timeout) => ({
299
+ id: timeout.id,
300
+ status: timeout.status,
301
+ dueAt: timeout.dueAt,
302
+ cancelRequestedAt: timeout.cancelRequestedAt,
303
+ pausedAt: timeout.pausedAt,
304
+ })),
305
+ });
277
306
  }
278
307
 
279
308
  /**
@@ -22,13 +22,6 @@ const USER_CHAT_STREAM_RECONNECT_DELAY_MS = 1_500;
22
22
  */
23
23
  const DISCONNECTED_CHAT_REFRESH_INTERVAL_MS = 4_000;
24
24
 
25
- /**
26
- * Periodic sidebar/list refresh cadence while the active chat stream is healthy.
27
- *
28
- * @private function of useAgentChatHistoryClientState
29
- */
30
- const CHAT_LIST_REFRESH_INTERVAL_MS = 20_000;
31
-
32
25
  /**
33
26
  * Inputs required to register side effects for durable chat-history synchronization.
34
27
  *
@@ -504,7 +497,7 @@ function keepActiveChatStreamConnected(params: {
504
497
  }
505
498
 
506
499
  /**
507
- * Refreshes the selected chat periodically and on tab visibility/focus changes.
500
+ * Refreshes the selected chat on disconnect polling and on tab visibility/focus changes.
508
501
  *
509
502
  * @private function of useAgentChatHistoryClientState
510
503
  */
@@ -529,9 +522,6 @@ function registerActiveChatRefreshPolling(params: {
529
522
  return undefined;
530
523
  }
531
524
 
532
- const pollIntervalMs = isActiveChatStreamConnected
533
- ? CHAT_LIST_REFRESH_INTERVAL_MS
534
- : DISCONNECTED_CHAT_REFRESH_INTERVAL_MS;
535
525
  const runRefresh = () => {
536
526
  if (typeof document !== 'undefined' && document.hidden) {
537
527
  return;
@@ -540,7 +530,9 @@ function registerActiveChatRefreshPolling(params: {
540
530
  void refreshActiveChat({ preserveDirtyDraft: true });
541
531
  };
542
532
 
543
- const interval = window.setInterval(runRefresh, pollIntervalMs);
533
+ const interval = isActiveChatStreamConnected
534
+ ? null
535
+ : window.setInterval(runRefresh, DISCONNECTED_CHAT_REFRESH_INTERVAL_MS);
544
536
  const handleVisibilityChange = () => {
545
537
  if (typeof document !== 'undefined' && !document.hidden) {
546
538
  runRefresh();
@@ -554,7 +546,9 @@ function registerActiveChatRefreshPolling(params: {
554
546
  window.addEventListener('focus', handleFocus);
555
547
 
556
548
  return () => {
557
- window.clearInterval(interval);
549
+ if (interval !== null) {
550
+ window.clearInterval(interval);
551
+ }
558
552
  document.removeEventListener('visibilitychange', handleVisibilityChange);
559
553
  window.removeEventListener('focus', handleFocus);
560
554
  };
@@ -52,7 +52,7 @@ export async function PATCH(request: Request) {
52
52
  }
53
53
 
54
54
  await updateVpsEnvironmentVariables({
55
- PTBK_AGENT: body.agent || '',
55
+ PTBK_HARNESS: body.agent || '',
56
56
  PTBK_MODEL: body.model || '',
57
57
  PTBK_THINKING_LEVEL: body.thinkingLevel || '',
58
58
  });
@@ -57,7 +57,7 @@ async function handleUserChatJobWorkerRequest(request: Request) {
57
57
 
58
58
  after(() =>
59
59
  triggerUserChatJobWorker({ origin }).catch((error) =>
60
- console.error('[user-chat-job] requeue failed', error),
60
+ console.warn('[user-chat-job] requeue failed', error),
61
61
  ),
62
62
  );
63
63
 
@@ -0,0 +1,7 @@
1
+ CREATE INDEX IF NOT EXISTS "prefix_UserChatJob_chatId_userId_agentPermanentId_active_createdAt_idx"
2
+ ON "prefix_UserChatJob" ("chatId", "userId", "agentPermanentId", "createdAt" ASC)
3
+ WHERE "status" IN ('QUEUED', 'RUNNING');
4
+
5
+ CREATE INDEX IF NOT EXISTS "prefix_UserChatTimeout_chatId_userId_agentPermanentId_active_dueAt_idx"
6
+ ON "prefix_UserChatTimeout" ("chatId", "userId", "agentPermanentId", "dueAt" ASC, "createdAt" ASC)
7
+ WHERE "status" IN ('QUEUED', 'RUNNING') AND "pausedAt" IS NULL;
@@ -0,0 +1,28 @@
1
+ import * as Sentry from '@sentry/nextjs';
2
+ import {
3
+ createSentrySdkTags,
4
+ resolveBrowserSentryDsn,
5
+ resolveSentrySdkEnvironment,
6
+ resolveSentrySdkRelease,
7
+ SENTRY_TRACE_PROPAGATION_TARGETS,
8
+ SENTRY_TRACES_SAMPLE_RATE,
9
+ } from './utils/errorReporting/sentrySdkConfig';
10
+
11
+ Sentry.init({
12
+ dsn: resolveBrowserSentryDsn(),
13
+ tracesSampleRate: SENTRY_TRACES_SAMPLE_RATE,
14
+ environment: resolveSentrySdkEnvironment(),
15
+ release: resolveSentrySdkRelease(),
16
+ integrations: [Sentry.browserTracingIntegration()],
17
+ tracePropagationTargets: SENTRY_TRACE_PROPAGATION_TARGETS,
18
+ initialScope: {
19
+ tags: createSentrySdkTags(),
20
+ },
21
+ });
22
+
23
+ /**
24
+ * Reports app-router navigation spans to Sentry.
25
+ *
26
+ * @private Sentry hook for Next.js client navigation instrumentation
27
+ */
28
+ export const onRouterTransitionStart = Sentry.captureRouterTransitionStart;
@@ -1,3 +1,5 @@
1
+ import * as Sentry from '@sentry/nextjs';
2
+
1
3
  /**
2
4
  * Registers startup hooks for the Agents Server runtime.
3
5
  *
@@ -5,11 +7,18 @@
5
7
  * @private internal startup hook for Agents Server runtime
6
8
  */
7
9
  export async function register(): Promise<void> {
10
+ if (process.env.NEXT_RUNTIME === 'edge') {
11
+ await import('./sentry.edge.config');
12
+ return;
13
+ }
14
+
8
15
  if (process.env.NEXT_RUNTIME !== 'nodejs') {
9
16
  return;
10
17
  }
11
18
 
12
19
  try {
20
+ await import('./sentry.server.config');
21
+
13
22
  const { registerServerErrorSentryLogging } = await import('./utils/errorReporting/registerServerErrorSentryLogging');
14
23
  registerServerErrorSentryLogging();
15
24
 
@@ -25,3 +34,10 @@ export async function register(): Promise<void> {
25
34
  });
26
35
  }
27
36
  }
37
+
38
+ /**
39
+ * Reports request errors from the Next.js app router to Sentry.
40
+ *
41
+ * @private Sentry hook for Next.js request instrumentation
42
+ */
43
+ export const onRequestError = Sentry.captureRequestError;
@@ -0,0 +1,18 @@
1
+ import * as Sentry from '@sentry/nextjs';
2
+ import {
3
+ createSentrySdkTags,
4
+ resolveBrowserSentryDsn,
5
+ resolveSentrySdkEnvironment,
6
+ resolveSentrySdkRelease,
7
+ SENTRY_TRACES_SAMPLE_RATE,
8
+ } from './utils/errorReporting/sentrySdkConfig';
9
+
10
+ Sentry.init({
11
+ dsn: resolveBrowserSentryDsn(),
12
+ tracesSampleRate: SENTRY_TRACES_SAMPLE_RATE,
13
+ environment: resolveSentrySdkEnvironment(),
14
+ release: resolveSentrySdkRelease(),
15
+ initialScope: {
16
+ tags: createSentrySdkTags(),
17
+ },
18
+ });
@@ -0,0 +1,19 @@
1
+ import * as Sentry from '@sentry/nextjs';
2
+ import { createAgentsServerSentryContext } from './utils/errorReporting/agentsServerSentryContext';
3
+ import { resolveServerSentryDsn, SENTRY_TRACES_SAMPLE_RATE } from './utils/errorReporting/sentrySdkConfig';
4
+
5
+ /**
6
+ * Shared diagnostic context applied to server-side SDK events at initialization time.
7
+ */
8
+ const agentsServerSentryContext = createAgentsServerSentryContext();
9
+
10
+ Sentry.init({
11
+ dsn: resolveServerSentryDsn(),
12
+ tracesSampleRate: SENTRY_TRACES_SAMPLE_RATE,
13
+ environment: agentsServerSentryContext.environment,
14
+ release: agentsServerSentryContext.release,
15
+ initialScope: {
16
+ tags: agentsServerSentryContext.tags,
17
+ extra: agentsServerSentryContext.extra,
18
+ },
19
+ });
@@ -102,6 +102,13 @@ async function resolveAgentRouteTargetUncached(
102
102
  };
103
103
  }
104
104
 
105
+ if (!options?.forceRefresh) {
106
+ const fastLocalRouteTarget = await resolveFastLocalAgentRouteTarget(normalizedReference, localServerUrl);
107
+ if (fastLocalRouteTarget) {
108
+ return fastLocalRouteTarget;
109
+ }
110
+ }
111
+
105
112
  const resolver = await $provideAgentReferenceResolver({ forceRefresh: options?.forceRefresh });
106
113
  let resolvedUrlValue: string;
107
114
 
@@ -145,6 +152,37 @@ async function resolveAgentRouteTargetUncached(
145
152
  };
146
153
  }
147
154
 
155
+ /**
156
+ * Resolves the common local-agent case without constructing the heavier shared reference resolver.
157
+ *
158
+ * Normal page and chat routes almost always target one local agent by its stored name or permanent id.
159
+ * Handling that case up front keeps route resolution cheap while still falling back to the full TEAM/federation
160
+ * resolver for aliases, remote agents, and fuzzy matches.
161
+ *
162
+ * @param reference - Normalized route/reference text.
163
+ * @param localServerUrl - Normalized URL of the current Agents Server instance.
164
+ * @returns Local route target or `null` when a direct lookup does not match.
165
+ */
166
+ async function resolveFastLocalAgentRouteTarget(
167
+ reference: string,
168
+ localServerUrl: string,
169
+ ): Promise<AgentRouteTarget | null> {
170
+ const collection = await $provideAgentCollectionForServer();
171
+ const directMatch = await collection.findAgentBasicInformation(reference);
172
+
173
+ if (!directMatch) {
174
+ return null;
175
+ }
176
+
177
+ const canonicalAgentId = directMatch.permanentId || directMatch.agentName;
178
+
179
+ return {
180
+ kind: 'local',
181
+ canonicalAgentId,
182
+ canonicalUrl: `${localServerUrl}${AGENT_PATH_PREFIX}${encodeURIComponent(canonicalAgentId)}`,
183
+ };
184
+ }
185
+
148
186
  /**
149
187
  * Memoized route-target resolver used for normal page rendering.
150
188
  */
@@ -37,7 +37,7 @@ export async function readConfiguredCodeRunner(): Promise<ConfiguredCodeRunner>
37
37
  >;
38
38
 
39
39
  return {
40
- agent: environmentByKey.PTBK_AGENT || process.env.PTBK_AGENT || 'github-copilot',
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
  };