@agent-native/core 0.219.0 → 0.220.0-nightly-20261023212848

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 (97) hide show
  1. package/dist/action.d.ts +23 -3
  2. package/dist/action.js +58 -3
  3. package/dist/agent/abort-reasons.d.ts +6 -0
  4. package/dist/agent/abort-reasons.js +6 -0
  5. package/dist/agent/execution-fence.js +5 -1
  6. package/dist/agent/production-agent.d.ts +22 -6
  7. package/dist/agent/production-agent.js +136 -37
  8. package/dist/agent/run-manager.js +92 -44
  9. package/dist/agent/run-store.d.ts +1 -0
  10. package/dist/agent/run-store.js +260 -31
  11. package/dist/agent/run-terminal-tracking.d.ts +29 -0
  12. package/dist/agent/run-terminal-tracking.js +56 -0
  13. package/dist/agent/run-turn-id-header.d.ts +9 -0
  14. package/dist/agent/run-turn-id-header.js +13 -0
  15. package/dist/agent/types.d.ts +11 -0
  16. package/dist/chat-threads/store.d.ts +23 -6
  17. package/dist/chat-threads/store.js +119 -36
  18. package/dist/cli/amplify-stream.js +1 -1
  19. package/dist/client/agent-chat/index.d.ts +2 -1
  20. package/dist/client/agent-chat/index.js +2 -1
  21. package/dist/client/agent-chat.d.ts +2 -0
  22. package/dist/client/agent-chat.js +31 -5
  23. package/dist/client/chat/agentkit-agent-native.js +8 -1
  24. package/dist/client/chat/agentkit-protocol.js +7 -1
  25. package/dist/client/chat/run-outcome-telemetry.js +1 -0
  26. package/dist/client/chat/run-outcome.d.ts +5 -0
  27. package/dist/client/chat/runtime.d.ts +34 -1
  28. package/dist/client/chat/runtime.js +96 -12
  29. package/dist/client/chat/turn-submitted-telemetry.d.ts +4 -0
  30. package/dist/client/chat/turn-submitted-telemetry.js +39 -0
  31. package/dist/client/chat/turn-submitted.d.ts +39 -0
  32. package/dist/client/chat/turn-submitted.js +16 -0
  33. package/dist/client/route-chunk-recovery.d.ts +3 -8
  34. package/dist/client/route-chunk-recovery.js +11 -9
  35. package/dist/client/use-chat-thread-list.d.ts +2 -0
  36. package/dist/client/use-chat-thread-list.js +48 -2
  37. package/dist/client/use-chat-threads.d.ts +13 -0
  38. package/dist/client/use-chat-threads.js +59 -4
  39. package/dist/collab/routes.d.ts +2 -2
  40. package/dist/db/client.d.ts +1 -0
  41. package/dist/db/client.js +28 -18
  42. package/dist/db/schema-snapshot.generated.js +5 -0
  43. package/dist/identity/rekey.d.ts +2 -0
  44. package/dist/identity/rekey.js +19 -0
  45. package/dist/index.d.ts +1 -1
  46. package/dist/index.js +1 -1
  47. package/dist/mcp/agent-connected.d.ts +5 -0
  48. package/dist/mcp/agent-connected.js +10 -3
  49. package/dist/mcp/analytics.d.ts +14 -1
  50. package/dist/mcp/analytics.js +54 -2
  51. package/dist/mcp/embed-app.js +2 -1
  52. package/dist/mcp/oauth-route.js +56 -1
  53. package/dist/notifications/routes.d.ts +1 -1
  54. package/dist/observability/routes.d.ts +7 -7
  55. package/dist/observability/traces.d.ts +2 -0
  56. package/dist/observability/traces.js +11 -2
  57. package/dist/provider-api/actions/custom-provider-registration.d.ts +16 -16
  58. package/dist/provider-api/actions/provider-api.d.ts +13 -13
  59. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  60. package/dist/resources/handlers.d.ts +1 -1
  61. package/dist/review/index.d.ts +1 -1
  62. package/dist/review/index.js +1 -1
  63. package/dist/review/suggestions/actions.js +25 -17
  64. package/dist/review/suggestions/store.d.ts +14 -1
  65. package/dist/review/suggestions/store.js +16 -3
  66. package/dist/review/suggestions/types.d.ts +15 -1
  67. package/dist/scripts/chat/search-chats.js +14 -2
  68. package/dist/server/agent-chat/browser-team-tools.js +39 -6
  69. package/dist/server/agent-chat/script-entries.js +44 -9
  70. package/dist/server/agent-chat-plugin.d.ts +2 -0
  71. package/dist/server/agent-chat-plugin.js +66 -16
  72. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  73. package/dist/server/agent-engine-default-model-route.d.ts +1 -1
  74. package/dist/server/first-touch-route.d.ts +2 -2
  75. package/dist/shared/route-chunk-recovery-bootstrap.d.ts +9 -0
  76. package/dist/shared/route-chunk-recovery-bootstrap.js +25 -0
  77. package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
  78. package/dist/templates/chat/.agents/skills/actions/SKILL.md +1 -0
  79. package/dist/templates/default/.agents/skills/actions/SKILL.md +1 -0
  80. package/dist/templates/factory/.agents/skills/review-prs/SKILL.md +27 -3
  81. package/dist/templates/headless/.agents/skills/actions/SKILL.md +1 -0
  82. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +1 -0
  83. package/dist/templates/workspace-core/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  84. package/dist/tracking/providers.js +3 -0
  85. package/dist/triggers/actions/manage-automation.d.ts +2 -2
  86. package/docs/content/external-agents.mdx +1 -1
  87. package/docs/content/mcp-apps.mdx +3 -3
  88. package/docs/content/mcp-protocol.mdx +1 -1
  89. package/docs/content/sharing.mdx +1 -0
  90. package/docs/content/template-assets-integrations.mdx +5 -3
  91. package/package.json +3 -3
  92. package/src/templates/chat/.agents/skills/actions/SKILL.md +1 -0
  93. package/src/templates/default/.agents/skills/actions/SKILL.md +1 -0
  94. package/src/templates/factory/.agents/skills/review-prs/SKILL.md +27 -3
  95. package/src/templates/headless/.agents/skills/actions/SKILL.md +1 -0
  96. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +1 -0
  97. package/src/templates/workspace-core/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
@@ -8,6 +8,7 @@ import { agentChatStreamingUrl, agentNativePath } from "../api-path.js";
8
8
  import { CHAT_REQUEST_TOO_LARGE_MESSAGE } from "../error-format.js";
9
9
  import { appendMissingFinalResponseWarning, findApprovalToolCallIndex, } from "../sse-event-processor.js";
10
10
  import { runOutcomeForCode } from "./run-outcome.js";
11
+ import { DEFAULT_TURN_SURFACE, } from "./turn-submitted.js";
11
12
  export const AGENT_NATIVE_RUN_RESUME_STATE_METADATA_KEY = "agentNativeRunResumeState";
12
13
  const AGENTKIT_TOOL_HISTORY_OMISSION_MESSAGE_ID_PREFIX = "agentkit-tool-history-omission";
13
14
  const DEFAULT_RUNTIME_CAPABILITIES = {
@@ -395,12 +396,13 @@ async function readHttpRuntimeError(response) {
395
396
  return error;
396
397
  }
397
398
  /**
398
- * A turn that failed to start keeps its id: the server records a turn it
399
- * refused under that id, and the client reports the failure with it.
399
+ * A turn that failed to start keeps its ids: the server records a turn it
400
+ * refused under the request's turn id, and the client reports the failure
401
+ * with both.
400
402
  */
401
- function withTurnId(error, turnId) {
403
+ function withTurnId(error, turnId, requestTurnId) {
402
404
  if (error !== null && typeof error === "object" && !("turnId" in error)) {
403
- Object.assign(error, { turnId });
405
+ Object.assign(error, { turnId, requestTurnId });
404
406
  }
405
407
  return error;
406
408
  }
@@ -439,6 +441,7 @@ export function createHttpAgentChatRuntime(options) {
439
441
  };
440
442
  const startTurn = async (turn) => {
441
443
  const turnId = turn.queuePromotion?.turnId ?? createRuntimeId("turn");
444
+ const requestTurnId = options.requestTurnId?.({ turn, turnId }) ?? turnId;
442
445
  const { controller, cleanup } = createAbortController(turn.abortSignal);
443
446
  latestTurn = turn;
444
447
  previousTurns.set(turnId, turn);
@@ -459,8 +462,17 @@ export function createHttpAgentChatRuntime(options) {
459
462
  headers,
460
463
  credentials: options.credentials,
461
464
  body: JSON.stringify(options.mapRequest
462
- ? options.mapRequest({ session: summary, turn, turnId })
463
- : defaultHttpRuntimeRequest({ session: summary, turn, turnId })),
465
+ ? options.mapRequest({
466
+ session: summary,
467
+ turn,
468
+ turnId,
469
+ requestTurnId,
470
+ })
471
+ : defaultHttpRuntimeRequest({
472
+ session: summary,
473
+ turn,
474
+ turnId: requestTurnId,
475
+ })),
464
476
  signal: controller.signal,
465
477
  });
466
478
  }
@@ -469,7 +481,7 @@ export function createHttpAgentChatRuntime(options) {
469
481
  if (!isRetryableRuntimeFailure(error)) {
470
482
  forgetTurnContext(turnId, turn);
471
483
  }
472
- throw withTurnId(error, turnId);
484
+ throw withTurnId(error, turnId, requestTurnId);
473
485
  }
474
486
  if (!response.ok) {
475
487
  cleanup();
@@ -477,7 +489,7 @@ export function createHttpAgentChatRuntime(options) {
477
489
  if (!isRetryableRuntimeFailure(error)) {
478
490
  forgetTurnContext(turnId, turn);
479
491
  }
480
- throw withTurnId(error, turnId);
492
+ throw withTurnId(error, turnId, requestTurnId);
481
493
  }
482
494
  if (response.status === 202) {
483
495
  let queuedSubmission;
@@ -505,7 +517,7 @@ export function createHttpAgentChatRuntime(options) {
505
517
  code: "invalid_queued_submission_response",
506
518
  status: response.status,
507
519
  retryable: true,
508
- }), turnId);
520
+ }), turnId, requestTurnId);
509
521
  }
510
522
  cleanup();
511
523
  forgetTurnContext(turnId, turn);
@@ -517,6 +529,29 @@ export function createHttpAgentChatRuntime(options) {
517
529
  };
518
530
  }
519
531
  const runId = response.headers.get("X-Run-Id") ?? undefined;
532
+ const replay = response.headers.get("X-Dispatch-Mode") === "replay";
533
+ // The server can store the run under another turn than the one asked
534
+ // for: an approval resume runs under its pending approval's turn. A
535
+ // replay streams a run an earlier request started, so without the
536
+ // server's turn its turn is unknown, not this request's.
537
+ const storedTurnId = response.headers.get("X-Turn-Id") || undefined;
538
+ const runTurnId = storedTurnId ?? (replay ? undefined : requestTurnId);
539
+ try {
540
+ // A replay streams a turn the server already finished, and a stream
541
+ // with no run id is a refusal (no model connected), not new work.
542
+ if (runId && !replay) {
543
+ options.onTurnAccepted?.({
544
+ session: summary,
545
+ turn,
546
+ turnId,
547
+ requestTurnId: storedTurnId ?? requestTurnId,
548
+ runId,
549
+ });
550
+ }
551
+ }
552
+ catch {
553
+ // coercion-ok: an observer of a started turn must not stop it.
554
+ }
520
555
  const events = (async function* () {
521
556
  let retryableFailure = false;
522
557
  try {
@@ -550,6 +585,7 @@ export function createHttpAgentChatRuntime(options) {
550
585
  };
551
586
  return {
552
587
  id: turnId,
588
+ requestTurnId: runTurnId,
553
589
  sessionId,
554
590
  runId,
555
591
  events,
@@ -3066,6 +3102,33 @@ function metadataStringList(metadata, key) {
3066
3102
  const strings = value.filter((item) => typeof item === "string" && item.length > 0);
3067
3103
  return strings.length > 0 ? strings : undefined;
3068
3104
  }
3105
+ /** The turn a continuation continues; a queued turn keeps its queue id. */
3106
+ function agentNativeRequestTurnId(input) {
3107
+ return (metadataString(input.turn.metadata, AGENT_NATIVE_CONTINUATION_TURN_ID_METADATA_KEY) ??
3108
+ input.turn.queuePromotion?.turnId ??
3109
+ input.turnId);
3110
+ }
3111
+ function agentNativeTurnInitiator(metadata) {
3112
+ const custom = asRecord(metadata?.custom);
3113
+ const startedByChat = metadata?.[AGENT_NATIVE_INTERNAL_CONTINUATION_METADATA_KEY] === true ||
3114
+ metadata?.trackInRunsTray === true ||
3115
+ metadata?.hideUserMessage === true ||
3116
+ [
3117
+ AGENT_NATIVE_CONTINUATION_TURN_ID_METADATA_KEY,
3118
+ AGENT_NATIVE_APPROVED_TOOL_CALLS_METADATA_KEY,
3119
+ "approvedToolCalls",
3120
+ AUTO_CONTINUE_OF_RUN_METADATA_KEY,
3121
+ CONTINUE_OF_RUN_METADATA_KEY,
3122
+ ].some((key) => metadata?.[key] !== undefined) ||
3123
+ custom?.agentNativeRecoveryAction !== undefined ||
3124
+ custom?.agentNativeResumeAfterSetup === true;
3125
+ if (startedByChat)
3126
+ return "system";
3127
+ // A send deferred while the thread restored is still the sender's own.
3128
+ const fromApp = metadataString(metadata, "submitMessageId") !== undefined ||
3129
+ custom?.agentNativeDeferredFromApp === true;
3130
+ return fromApp ? "app" : "user";
3131
+ }
3069
3132
  function terminalRuntimeTurn(input) {
3070
3133
  return {
3071
3134
  id: input.turnId ?? createRuntimeId("turn"),
@@ -3075,7 +3138,7 @@ function terminalRuntimeTurn(input) {
3075
3138
  })(),
3076
3139
  };
3077
3140
  }
3078
- export function createAgentNativeChatRuntime(options = {}) {
3141
+ export function createAgentNativeChatRuntime(options = {}, telemetry = {}) {
3079
3142
  const apiUrl = options.apiUrl ?? agentNativePath("/_agent-native/agent-chat");
3080
3143
  const runtimeId = options.id ?? "agent-native";
3081
3144
  const fetchImpl = options.fetch ?? fetch;
@@ -3157,6 +3220,27 @@ export function createAgentNativeChatRuntime(options = {}) {
3157
3220
  description: options.description ?? "Agent-Native's built-in chat transport.",
3158
3221
  endpoint: apiUrl,
3159
3222
  fetch: runtimeFetch,
3223
+ requestTurnId: agentNativeRequestTurnId,
3224
+ onTurnAccepted: ({ session, turn, requestTurnId, runId }) => {
3225
+ const metadata = turn.metadata;
3226
+ const custom = asRecord(metadata?.custom) ?? undefined;
3227
+ const requestMode = metadataString(metadata, "requestMode") ??
3228
+ metadataString(metadata, "mode") ??
3229
+ options.mode;
3230
+ telemetry.onTurnSubmitted?.({
3231
+ turnId: requestTurnId,
3232
+ threadId: session.threadId ?? options.threadId,
3233
+ runId,
3234
+ surface: metadataString(metadata, "agentNativeSurface") ??
3235
+ DEFAULT_TURN_SURFACE,
3236
+ initiator: agentNativeTurnInitiator(metadata),
3237
+ submitMessageId: metadataString(metadata, "submitMessageId"),
3238
+ recoveryOfRunId: metadataString(custom, "agentNativeRecoveryOfRunId") ??
3239
+ metadataString(metadata, CONTINUE_OF_RUN_METADATA_KEY) ??
3240
+ metadataString(metadata, AUTO_CONTINUE_OF_RUN_METADATA_KEY),
3241
+ requestMode,
3242
+ });
3243
+ },
3160
3244
  beforeStartTurn: async ({ session, turn, turnId }) => {
3161
3245
  const metadata = turn.metadata;
3162
3246
  const isAdmittedContinuation = turn.queuePromotion !== undefined ||
@@ -3213,7 +3297,7 @@ export function createAgentNativeChatRuntime(options = {}) {
3213
3297
  connectionRequests: true,
3214
3298
  },
3215
3299
  },
3216
- mapRequest: ({ session, turn, turnId }) => {
3300
+ mapRequest: ({ session, turn, turnId, requestTurnId }) => {
3217
3301
  const latestUserMessage = [...(turn.messages ?? [])]
3218
3302
  .reverse()
3219
3303
  .find((message) => message.role === "user");
@@ -3265,7 +3349,7 @@ export function createAgentNativeChatRuntime(options = {}) {
3265
3349
  history,
3266
3350
  ...(structuredHistory?.length ? { structuredHistory } : {}),
3267
3351
  ...(loadedSkillSlugs.length ? { loadedSkillSlugs } : {}),
3268
- turnId: continuationTurnId ?? turn.queuePromotion?.turnId ?? turnId,
3352
+ turnId: requestTurnId,
3269
3353
  threadId: session.threadId ?? options.threadId,
3270
3354
  ...(turn.submissionPayload !== undefined
3271
3355
  ? { submissionPayload: turn.submissionPayload }
@@ -0,0 +1,4 @@
1
+ import { trackEvent } from "../analytics.js";
2
+ import { type RoutedTurnSubmittedReport, type TurnSubmittedReport } from "./turn-submitted.js";
3
+ export declare function trackTurnSubmitted(report: TurnSubmittedReport, send?: typeof trackEvent): void;
4
+ export declare function trackRoutedTurnSubmitted(report: RoutedTurnSubmittedReport, send?: typeof trackEvent): void;
@@ -0,0 +1,39 @@
1
+ import { trackEvent } from "../analytics.js";
2
+ import { TURN_SUBMITTED_EVENT, } from "./turn-submitted.js";
3
+ export function trackTurnSubmitted(report, send = trackEvent) {
4
+ try {
5
+ send(TURN_SUBMITTED_EVENT, {
6
+ turn_id: report.turnId,
7
+ ...(report.threadId ? { thread_id: report.threadId } : {}),
8
+ ...(report.runId ? { run_id: report.runId } : {}),
9
+ surface: report.surface,
10
+ initiator: report.initiator,
11
+ ...(report.submitMessageId
12
+ ? { submit_message_id: report.submitMessageId }
13
+ : {}),
14
+ ...(report.recoveryOfRunId
15
+ ? { recovery_of_run_id: report.recoveryOfRunId }
16
+ : {}),
17
+ ...(report.requestMode ? { request_mode: report.requestMode } : {}),
18
+ });
19
+ }
20
+ catch {
21
+ // coercion-ok: telemetry must never change whether a turn runs.
22
+ }
23
+ }
24
+ export function trackRoutedTurnSubmitted(report, send = trackEvent) {
25
+ try {
26
+ send(TURN_SUBMITTED_EVENT, {
27
+ routed_to: report.routedTo,
28
+ surface: report.surface,
29
+ initiator: "app",
30
+ ...(report.submitMessageId
31
+ ? { submit_message_id: report.submitMessageId }
32
+ : {}),
33
+ ...(report.requestMode ? { request_mode: report.requestMode } : {}),
34
+ });
35
+ }
36
+ catch {
37
+ // coercion-ok: telemetry must never change where a message is sent.
38
+ }
39
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * One event per agent request the server accepts, carrying the turn id the
3
+ * server stores on that request's run so the two join. A turn's continuations
4
+ * are further requests: they repeat its `turn_id` as `initiator: "system"`, so
5
+ * count turns by distinct `turn_id`, and the turns people started by
6
+ * `initiator` `user` or `app`. A request refused for a busy run slot and then
7
+ * queued is counted once when the browser promotes it, and not yet when the
8
+ * server's inbox drain admits it. A replay is not counted, including a retry
9
+ * the server answers with the run it already started. A send routed to
10
+ * Builder or an MCP host never starts a local turn; it is reported with
11
+ * `routed_to` and no `turn_id`, also when the host has not confirmed it.
12
+ * Never the message text.
13
+ */
14
+ export declare const TURN_SUBMITTED_EVENT = "agent_turn_submitted";
15
+ /**
16
+ * `system` is a turn the chat started on its own: a recovery, continuation,
17
+ * approval, setup replay, or background send. `app` came from app code through
18
+ * `sendToAgentChat`, including one deferred while the thread restored. `user`
19
+ * is everything else the person typed or picked.
20
+ */
21
+ export type TurnInitiator = "user" | "app" | "system";
22
+ /** The surface a turn reports when nothing names one (the legacy `chat_submit` default). */
23
+ export declare const DEFAULT_TURN_SURFACE = "chat";
24
+ export interface TurnSubmittedReport {
25
+ readonly turnId: string;
26
+ readonly threadId?: string;
27
+ readonly runId?: string;
28
+ readonly surface: string;
29
+ readonly initiator: TurnInitiator;
30
+ readonly submitMessageId?: string;
31
+ readonly recoveryOfRunId?: string;
32
+ readonly requestMode?: string;
33
+ }
34
+ export interface RoutedTurnSubmittedReport {
35
+ readonly routedTo: "builder" | "mcp_host";
36
+ readonly surface: string;
37
+ readonly submitMessageId?: string;
38
+ readonly requestMode?: string;
39
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * One event per agent request the server accepts, carrying the turn id the
3
+ * server stores on that request's run so the two join. A turn's continuations
4
+ * are further requests: they repeat its `turn_id` as `initiator: "system"`, so
5
+ * count turns by distinct `turn_id`, and the turns people started by
6
+ * `initiator` `user` or `app`. A request refused for a busy run slot and then
7
+ * queued is counted once when the browser promotes it, and not yet when the
8
+ * server's inbox drain admits it. A replay is not counted, including a retry
9
+ * the server answers with the run it already started. A send routed to
10
+ * Builder or an MCP host never starts a local turn; it is reported with
11
+ * `routed_to` and no `turn_id`, also when the host has not confirmed it.
12
+ * Never the message text.
13
+ */
14
+ export const TURN_SUBMITTED_EVENT = "agent_turn_submitted";
15
+ /** The surface a turn reports when nothing names one (the legacy `chat_submit` default). */
16
+ export const DEFAULT_TURN_SURFACE = "chat";
@@ -1,11 +1,6 @@
1
- /**
2
- * Fired on `window` the first time a stale chunk could not be recovered by a
3
- * reload. The shared noise rules drop the raw dynamic-import failure because
4
- * recovery handles it; this is the one signal that it did not, so a deploy whose
5
- * chunks are gone for everyone stays visible.
6
- */
7
- export declare const STALE_CHUNK_RECOVERY_EXHAUSTED_EVENT = "agent-native:stale-chunk-recovery-exhausted";
8
- export type StaleChunkRecoveryExhaustedReason = "cooldown" | "desktop";
1
+ import { STALE_CHUNK_RECOVERY_EXHAUSTED_EVENT } from "../shared/route-chunk-recovery-bootstrap.js";
2
+ export { STALE_CHUNK_RECOVERY_EXHAUSTED_EVENT };
3
+ export type StaleChunkRecoveryExhaustedReason = "cooldown" | "desktop" | "external-embed";
9
4
  export interface StaleChunkRecoveryExhausted {
10
5
  reason: StaleChunkRecoveryExhaustedReason;
11
6
  }
@@ -1,16 +1,9 @@
1
- import { CHUNK_RECOVERY_CACHE_BUSTER_PARAM, CHUNK_RECOVERY_ORIGINAL_HASH_PARAM, CHUNK_RECOVERY_PATH_SUFFIX, CHUNK_RECOVERY_QUERY_PARAM, CHUNK_RECOVERY_QUERY_VALUE, ROUTE_WARMUP_PRELOAD_ATTRIBUTE, STALE_CHUNK_RELOAD_AT_KEY, STALE_CHUNK_RELOAD_COOLDOWN_MS, } from "../shared/route-chunk-recovery-bootstrap.js";
1
+ import { CHUNK_RECOVERY_CACHE_BUSTER_PARAM, CHUNK_RECOVERY_ORIGINAL_HASH_PARAM, CHUNK_RECOVERY_PATH_SUFFIX, CHUNK_RECOVERY_QUERY_PARAM, CHUNK_RECOVERY_QUERY_VALUE, EXTERNAL_EMBED_WINDOW_KEY, ROUTE_WARMUP_PRELOAD_ATTRIBUTE, STALE_CHUNK_EXHAUSTED_KEY, STALE_CHUNK_RECOVERY_EXHAUSTED_EVENT, STALE_CHUNK_RELOAD_AT_KEY, STALE_CHUNK_RELOAD_COOLDOWN_MS, } from "../shared/route-chunk-recovery-bootstrap.js";
2
2
  import { recordNav } from "./recorder.js";
3
3
  const INSTALL_KEY = "__agentNativeRouteChunkRecoveryInstalled";
4
4
  const INTENDED_NAV_MAX_AGE_MS = 15_000;
5
- const STALE_CHUNK_EXHAUSTED_KEY = "__agentNativeStaleChunkRecoveryExhausted";
6
5
  const recoveryStates = new WeakMap();
7
- /**
8
- * Fired on `window` the first time a stale chunk could not be recovered by a
9
- * reload. The shared noise rules drop the raw dynamic-import failure because
10
- * recovery handles it; this is the one signal that it did not, so a deploy whose
11
- * chunks are gone for everyone stays visible.
12
- */
13
- export const STALE_CHUNK_RECOVERY_EXHAUSTED_EVENT = "agent-native:stale-chunk-recovery-exhausted";
6
+ export { STALE_CHUNK_RECOVERY_EXHAUSTED_EVENT };
14
7
  export function createRouteChunkRecoveryState() {
15
8
  return {
16
9
  intendedHref: null,
@@ -187,6 +180,9 @@ function isModuleAssetLoadFailure(event) {
187
180
  function isAgentNativeDesktop(win) {
188
181
  return /AgentNativeDesktop/i.test(win.navigator?.userAgent || "");
189
182
  }
183
+ function isExternalEmbed(win) {
184
+ return Boolean(win[EXTERNAL_EMBED_WINDOW_KEY]);
185
+ }
190
186
  function hasViteDevRecovery(win) {
191
187
  if (win["__agentNativeViteDevRecoveryInstalled"] === true) {
192
188
  return true;
@@ -269,6 +265,10 @@ export function reloadForStaleChunk(win = typeof window === "undefined" ? undefi
269
265
  reportStaleChunkRecoveryExhausted(win, "desktop");
270
266
  return false;
271
267
  }
268
+ if (isExternalEmbed(win)) {
269
+ reportStaleChunkRecoveryExhausted(win, "external-embed");
270
+ return false;
271
+ }
272
272
  const lastReloadAt = readStaleChunkReloadAt(win);
273
273
  if (lastReloadAt > 0 &&
274
274
  now - lastReloadAt <= STALE_CHUNK_RELOAD_COOLDOWN_MS) {
@@ -288,6 +288,8 @@ export function recoverFromStaleChunkError(error, win = typeof window === "undef
288
288
  return reloadForStaleChunk(win);
289
289
  }
290
290
  function recoverToIntendedNavigation(win, state) {
291
+ if (isExternalEmbed(win))
292
+ return false;
291
293
  const target = getFreshIntendedNavigation(state, win.location.href);
292
294
  const sameCurrentTarget = isAgentNativeDesktop(win) &&
293
295
  !target &&
@@ -8,6 +8,7 @@ export interface UseChatThreadListOptions {
8
8
  includeExternal?: boolean;
9
9
  /** Include archived threads. Defaults to false. */
10
10
  includeArchived?: boolean;
11
+ includeHidden?: boolean;
11
12
  /** Search text. Empty lists the whole set. */
12
13
  query?: string;
13
14
  /** When false nothing is fetched and the list is empty. Defaults to true. */
@@ -30,6 +31,7 @@ export interface UseChatThreadListResult {
30
31
  refresh: () => Promise<void>;
31
32
  /** Reports whether this invocation applied the complete refreshed window. */
32
33
  refreshWithResult: () => Promise<ChatThreadListRefreshOutcome>;
34
+ hideThread: (id: string, hidden: boolean) => Promise<boolean>;
33
35
  }
34
36
  /** `pageSize` is not an integer between 1 and the server's page cap. */
35
37
  export declare class ChatThreadListConfigError extends Error {
@@ -1,6 +1,6 @@
1
1
  import { useCallback, useEffect, useLayoutEffect, useState } from "react";
2
2
  import { agentNativePath } from "./api-path.js";
3
- import { CHAT_THREADS_MAX_PAGE_SIZE, CHAT_THREADS_UPDATED_EVENT, ChatThreadListError, fetchChatThreadListPage, } from "./use-chat-threads.js";
3
+ import { CHAT_THREADS_MAX_PAGE_SIZE, CHAT_THREADS_UPDATED_EVENT, ChatThreadListError, fetchChatThreadListPage, hideChatThread, } from "./use-chat-threads.js";
4
4
  const DEFAULT_PAGE_SIZE = 50;
5
5
  /** `pageSize` is not an integer between 1 and the server's page cap. */
6
6
  export class ChatThreadListConfigError extends Error {
@@ -48,7 +48,7 @@ function createListState(identity, enabled) {
48
48
  * rather than collapsing to the first page.
49
49
  */
50
50
  export function useChatThreadList(options) {
51
- const { ownership, includeExternal = false, includeArchived = false, enabled = true, refreshOnThreadUpdates = true, } = options;
51
+ const { ownership, includeExternal = false, includeArchived = false, includeHidden = false, enabled = true, refreshOnThreadUpdates = true, } = options;
52
52
  const apiUrl = options.apiUrl ?? agentNativePath("/_agent-native/agent-chat");
53
53
  const pageSize = resolvePageSize(options.pageSize);
54
54
  const query = options.query?.trim() ?? "";
@@ -63,6 +63,7 @@ export function useChatThreadList(options) {
63
63
  pageSize,
64
64
  includeExternal,
65
65
  includeArchived,
66
+ includeHidden,
66
67
  enabled,
67
68
  ]);
68
69
  const [stored, setState] = useState(() => createListState(identity, enabled));
@@ -91,6 +92,7 @@ export function useChatThreadList(options) {
91
92
  ownership,
92
93
  includeExternal,
93
94
  includeArchived,
95
+ includeHidden,
94
96
  scope: scopeType && scopeId ? { type: scopeType, id: scopeId } : null,
95
97
  }).then((result) => {
96
98
  if (result.hasMore === undefined) {
@@ -106,6 +108,7 @@ export function useChatThreadList(options) {
106
108
  apiUrl,
107
109
  includeArchived,
108
110
  includeExternal,
111
+ includeHidden,
109
112
  ownership,
110
113
  query,
111
114
  scopeId,
@@ -223,6 +226,48 @@ export function useChatThreadList(options) {
223
226
  return () => window.removeEventListener(CHAT_THREADS_UPDATED_EVENT, onUpdated);
224
227
  }, [enabled, refresh, refreshOnThreadUpdates]);
225
228
  const { threads, loading, loadingMore, error, hasMore } = state;
229
+ const hideThread = useCallback(async (id, hidden) => {
230
+ const previous = state.threads.find((thread) => thread.id === id);
231
+ update({
232
+ threads: state.threads.flatMap((thread) => {
233
+ if (thread.id !== id)
234
+ return [thread];
235
+ if (hidden && !includeHidden)
236
+ return [];
237
+ return [{ ...thread, hiddenAt: hidden ? Date.now() : null }];
238
+ }),
239
+ });
240
+ try {
241
+ return await hideChatThread(id, hidden, {
242
+ apiUrl,
243
+ scope: options.scope,
244
+ });
245
+ }
246
+ catch (error) {
247
+ // A newer refresh may have dropped a removed row because access was
248
+ // revoked, so the server decides whether it comes back.
249
+ if (hidden && !includeHidden)
250
+ void refreshWithResult();
251
+ else if (previous)
252
+ setState((current) => current.session === session
253
+ ? {
254
+ ...current,
255
+ threads: current.threads.map((thread) => thread.id === id
256
+ ? { ...thread, hiddenAt: previous.hiddenAt }
257
+ : thread),
258
+ }
259
+ : current);
260
+ throw error;
261
+ }
262
+ }, [
263
+ apiUrl,
264
+ includeHidden,
265
+ options.scope,
266
+ refreshWithResult,
267
+ session,
268
+ state.threads,
269
+ update,
270
+ ]);
226
271
  return {
227
272
  threads,
228
273
  loading,
@@ -232,5 +277,6 @@ export function useChatThreadList(options) {
232
277
  loadMore,
233
278
  refresh,
234
279
  refreshWithResult,
280
+ hideThread,
235
281
  };
236
282
  }
@@ -24,6 +24,7 @@ export interface ChatThreadSummary {
24
24
  scope: ChatThreadScope | null;
25
25
  source?: ChatThreadSource | null;
26
26
  pinnedAt?: number | null;
27
+ hiddenAt?: number | null;
27
28
  archivedAt?: number | null;
28
29
  /**
29
30
  * Server-supplied on listed threads. Absent on optimistic local drafts,
@@ -51,6 +52,7 @@ export interface ChatThreadData {
51
52
  scope: ChatThreadScope | null;
52
53
  source?: ChatThreadSource | null;
53
54
  pinnedAt?: number | null;
55
+ hiddenAt?: number | null;
54
56
  archivedAt?: number | null;
55
57
  }
56
58
  export interface ChatThreadSnapshot {
@@ -76,6 +78,7 @@ export interface UseChatThreadsOptions {
76
78
  browserTabId?: string;
77
79
  routeThreadId?: string | null;
78
80
  includeExternal?: boolean;
81
+ includeHidden?: boolean;
79
82
  isolateHistoryByScope?: boolean;
80
83
  /** Narrows the history list and search. Defaults to `all`. */
81
84
  ownership?: ChatThreadOwnership;
@@ -99,6 +102,7 @@ export interface ChatThreadListPageRequest {
99
102
  ownership?: ChatThreadOwnership;
100
103
  includeExternal?: boolean;
101
104
  includeArchived?: boolean;
105
+ includeHidden?: boolean;
102
106
  scope?: ChatThreadScope | null;
103
107
  }
104
108
  /** A listed thread as the server returns it: ownership metadata always present. */
@@ -124,6 +128,14 @@ export interface ChatThreadListPage {
124
128
  * of returned rows and use `hasMore`, rather than the requested limit.
125
129
  */
126
130
  export declare function fetchChatThreadListPage(request?: ChatThreadListPageRequest): Promise<ChatThreadListPage>;
131
+ export declare class ChatThreadMutationError extends Error {
132
+ readonly status: number;
133
+ constructor(status: number);
134
+ }
135
+ export declare function hideChatThread(threadId: string, hidden: boolean, options?: {
136
+ apiUrl?: string;
137
+ scope?: ChatThreadScope | null;
138
+ }): Promise<boolean>;
127
139
  export declare function appendChatThreadScopeParams(params: URLSearchParams, scope?: ChatThreadScope | null): void;
128
140
  /**
129
141
  * Tell every mounted chat history list the thread set or its sharing changed,
@@ -141,6 +153,7 @@ export declare function useChatThreads(apiUrl?: string, storageKey?: string, sco
141
153
  deleteThread: (id: string) => Promise<void>;
142
154
  detachThread: (threadId: string) => Promise<void>;
143
155
  pinThread: (threadId: string, pinned: boolean) => Promise<boolean>;
156
+ hideThread: (threadId: string, hidden: boolean) => Promise<boolean>;
144
157
  archiveThread: (threadId: string) => Promise<boolean>;
145
158
  renameThread: (threadId: string, title: string) => Promise<boolean>;
146
159
  forkThread: (sourceId: string, sourceSnapshot?: ChatThreadSnapshot | null) => Promise<string | null>;