@agent-native/core 0.227.0 → 0.228.0-nightly-20261110212400

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 (84) hide show
  1. package/dist/agent/reaped-run-thread.js +6 -0
  2. package/dist/application-state/handlers.d.ts +3 -3
  3. package/dist/application-state/handlers.js +6 -1
  4. package/dist/application-state/index.d.ts +3 -2
  5. package/dist/application-state/index.js +3 -2
  6. package/dist/application-state/last-used-tab.d.ts +11 -0
  7. package/dist/application-state/last-used-tab.js +43 -0
  8. package/dist/application-state/script-helpers.d.ts +26 -0
  9. package/dist/application-state/script-helpers.js +83 -15
  10. package/dist/application-state/store.d.ts +8 -0
  11. package/dist/application-state/store.js +16 -0
  12. package/dist/client/agent-sidebar-state.d.ts +2 -0
  13. package/dist/client/browser-tab-use.d.ts +9 -0
  14. package/dist/client/browser-tab-use.js +112 -0
  15. package/dist/client/navigation/index.d.ts +1 -0
  16. package/dist/client/navigation/index.js +1 -0
  17. package/dist/client/review/use-review.d.ts +28 -0
  18. package/dist/client/review/use-review.js +41 -12
  19. package/dist/client/route-state.js +2 -0
  20. package/dist/client/use-db-sync.d.ts +8 -1
  21. package/dist/client/use-db-sync.js +6 -5
  22. package/dist/collab/routes.d.ts +1 -1
  23. package/dist/collab/struct-routes.d.ts +1 -1
  24. package/dist/db/schema-snapshot.generated.js +6 -1
  25. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  26. package/dist/file-upload/index.d.ts +1 -1
  27. package/dist/file-upload/index.js +1 -1
  28. package/dist/file-upload/s3.d.ts +9 -0
  29. package/dist/file-upload/s3.js +14 -1
  30. package/dist/framework-package-versions.json +7 -7
  31. package/dist/jobs/scheduler.js +4 -35
  32. package/dist/mcp/browser-tab-target.d.ts +16 -0
  33. package/dist/mcp/browser-tab-target.js +58 -0
  34. package/dist/mcp/build-server.js +9 -2
  35. package/dist/notifications/routes.d.ts +3 -3
  36. package/dist/org/handlers.d.ts +1 -1
  37. package/dist/progress/routes.d.ts +1 -1
  38. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  39. package/dist/provider-api/actions/provider-api.d.ts +7 -7
  40. package/dist/resources/handlers.d.ts +1 -1
  41. package/dist/review/actions/create-review-comment.d.ts +7 -0
  42. package/dist/review/actions/create-review-comment.js +29 -3
  43. package/dist/review/actions/delete-review-comment.js +2 -2
  44. package/dist/review/actions/list-review-comments.d.ts +3 -0
  45. package/dist/review/actions/list-review-comments.js +41 -17
  46. package/dist/review/actions/react-to-review-comment.js +2 -2
  47. package/dist/review/actions/reply-review-comment.d.ts +4 -0
  48. package/dist/review/actions/reply-review-comment.js +15 -6
  49. package/dist/review/actions/update-review-comment-anchor.d.ts +6 -0
  50. package/dist/review/actions/update-review-comment-anchor.js +39 -10
  51. package/dist/review/actions/update-review-comment.d.ts +3 -0
  52. package/dist/review/actions/update-review-comment.js +28 -3
  53. package/dist/review/errors.d.ts +33 -0
  54. package/dist/review/errors.js +51 -0
  55. package/dist/review/index.d.ts +5 -4
  56. package/dist/review/index.js +3 -2
  57. package/dist/review/registry.d.ts +51 -1
  58. package/dist/review/registry.js +117 -0
  59. package/dist/review/store.d.ts +59 -14
  60. package/dist/review/store.js +622 -101
  61. package/dist/review/suggestions/actions.js +10 -8
  62. package/dist/review/types.d.ts +89 -0
  63. package/dist/search/query.d.ts +11 -5
  64. package/dist/search/query.js +52 -15
  65. package/dist/search/tokenize.d.ts +1 -0
  66. package/dist/search/tokenize.js +3 -0
  67. package/dist/secrets/routes.d.ts +2 -2
  68. package/dist/server/request-context.d.ts +26 -0
  69. package/dist/shared/app-state-routes.d.ts +7 -0
  70. package/dist/shared/app-state-routes.js +7 -0
  71. package/dist/shared/runtime-config.js +5 -1
  72. package/dist/templates/chat/.agents/skills/context-awareness/SKILL.md +13 -0
  73. package/dist/templates/default/.agents/skills/context-awareness/SKILL.md +13 -0
  74. package/dist/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +13 -0
  75. package/dist/triggers/condition-evaluator.d.ts +2 -0
  76. package/dist/triggers/condition-evaluator.js +93 -3
  77. package/dist/triggers/dispatcher.js +24 -6
  78. package/docs/content/create-with-an-agent.mdx +10 -4
  79. package/docs/content/durable-resume.mdx +11 -2
  80. package/docs/content/toolkit-comments-review.mdx +54 -1
  81. package/package.json +3 -3
  82. package/src/templates/chat/.agents/skills/context-awareness/SKILL.md +13 -0
  83. package/src/templates/default/.agents/skills/context-awareness/SKILL.md +13 -0
  84. package/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +13 -0
@@ -256,6 +256,9 @@ function matchesCommentQuery(params, comment) {
256
256
  const query = params;
257
257
  if (query.targetId !== undefined && query.targetId !== comment.targetId)
258
258
  return false;
259
+ if (query.surfaceId !== undefined &&
260
+ query.surfaceId !== (comment.surfaceId ?? null))
261
+ return false;
259
262
  if (comment.status === "resolved" && !query.includeResolved)
260
263
  return false;
261
264
  if (comment.status === "deleted" && !query.includeDeleted)
@@ -274,9 +277,16 @@ function optimisticComment(input, id, parent) {
274
277
  parentCommentId: parent?.id ?? null,
275
278
  targetId: parent?.targetId ??
276
279
  ("targetId" in input ? (input.targetId ?? null) : null),
280
+ surfaceId: parent?.surfaceId ??
281
+ ("surfaceId" in input ? (input.surfaceId ?? null) : null),
277
282
  kind: parent?.kind ?? ("kind" in input ? (input.kind ?? "comment") : "comment"),
278
283
  status: "open",
279
284
  anchor: parent?.anchor ?? ("anchor" in input ? (input.anchor ?? null) : null),
285
+ anchorKind: parent?.anchorKind ??
286
+ ("anchorKind" in input ? (input.anchorKind ?? null) : null),
287
+ anchorVersion: parent?.anchorVersion ??
288
+ ("anchorVersion" in input ? (input.anchorVersion ?? null) : null),
289
+ revision: 1,
280
290
  body: input.body,
281
291
  authorEmail: null,
282
292
  authorName: input.authorName ?? null,
@@ -300,6 +310,35 @@ function optimisticComment(input, id, parent) {
300
310
  function replaceComment(comments, previousId, replacement) {
301
311
  return comments.map((comment) => comment.id === previousId ? replacement : comment);
302
312
  }
313
+ /**
314
+ * Adds a new comment to a cached list. Oldest first, it lists last, so a page
315
+ * with more after it is left alone; the comment shows on the last page.
316
+ */
317
+ export function addOptimisticComment(data, params, comment) {
318
+ if (!matchesCommentQuery(params, comment))
319
+ return data;
320
+ if (isReviewCommentsResult(data) && data.nextCursor)
321
+ return data;
322
+ return updateComments(data, (comments) => insertOptimisticComment(comments, comment, params));
323
+ }
324
+ /**
325
+ * Adds a reply to a cached list that holds its parent. Oldest first, a new
326
+ * reply lists after every comment there is, so like a new comment it goes
327
+ * only on the last page.
328
+ */
329
+ export function addOptimisticReply(data, params, parentId, reply) {
330
+ if (isReviewCommentsResult(data) && data.nextCursor)
331
+ return data;
332
+ return updateComments(data, (comments) => {
333
+ const parent = comments.find((comment) => comment.id === parentId);
334
+ if (!parent)
335
+ return comments;
336
+ const next = reply(parent);
337
+ return matchesCommentQuery(params, next)
338
+ ? insertOptimisticComment(comments, next, params)
339
+ : comments;
340
+ });
341
+ }
303
342
  export function insertOptimisticComment(comments, comment, params) {
304
343
  if (comments.some((item) => item.id === comment.id))
305
344
  return comments;
@@ -430,9 +469,7 @@ export function useCreateReviewComment() {
430
469
  return {
431
470
  action: "list-review-comments",
432
471
  resource: input,
433
- transform: (data, params) => matchesCommentQuery(params, comment)
434
- ? updateComments(data, (comments) => insertOptimisticComment(comments, comment, params))
435
- : data,
472
+ transform: (data, params) => addOptimisticComment(data, params, comment),
436
473
  onSuccess: (result) => (data) => updateComments(data, (comments) => replaceComment(comments, id, result)),
437
474
  };
438
475
  });
@@ -443,15 +480,7 @@ export function useReplyReviewComment() {
443
480
  return {
444
481
  action: "list-review-comments",
445
482
  resource: input,
446
- transform: (data, params) => updateComments(data, (comments) => {
447
- const parent = comments.find((comment) => comment.id === input.commentId);
448
- if (!parent)
449
- return comments;
450
- const reply = optimisticComment(input, id, parent);
451
- return matchesCommentQuery(params, reply)
452
- ? insertOptimisticComment(comments, reply, params)
453
- : comments;
454
- }),
483
+ transform: (data, params) => addOptimisticReply(data, params, input.commentId, (parent) => optimisticComment(input, id, parent)),
455
484
  onSuccess: (result) => (data) => updateComments(data, (comments) => replaceComment(comments, id, result)),
456
485
  };
457
486
  });
@@ -4,6 +4,7 @@ import { useLocation, useNavigate, } from "react-router";
4
4
  import { isWorkspaceAppPath } from "./api-path.js";
5
5
  import { deleteClientAppState, readClientAppState, setClientAppState, } from "./application-state.js";
6
6
  import { getBrowserTabId, isBrowserTabIdForCurrentDocument, } from "./browser-tab-id.js";
7
+ import { useBrowserTabUse } from "./browser-tab-use.js";
7
8
  import { navigateWithAgentChatViewTransition, } from "./chat-view-transition.js";
8
9
  import { postAgentNativeWorkspaceAppRoute } from "./workspace-app-navigation.js";
9
10
  const SAFE_BROWSER_TAB_ID_RE = /^[A-Za-z0-9_-]{1,96}$/;
@@ -220,6 +221,7 @@ export function useAgentRouteState(options) {
220
221
  return;
221
222
  postAgentNativeWorkspaceAppRoute(`${location.pathname}${location.search}${location.hash}`);
222
223
  }, [location.hash, location.pathname, location.search, options.enabled]);
224
+ useBrowserTabUse(`${location.pathname}${location.search}`, options.enabled !== false);
223
225
  const navigationKeys = useMemo(() => {
224
226
  const scopedKey = appStateKeyForBrowserTab(navigationKey, browserTabId);
225
227
  const keys = [scopedKey];
@@ -53,9 +53,16 @@ export type SyncEvent = {
53
53
  requestSource?: string;
54
54
  [k: string]: unknown;
55
55
  };
56
- /** What a logged sync invalidation asked: a key prefix, or every query. */
56
+ /**
57
+ * What a logged sync invalidation asked: a key prefix, a predicate the query
58
+ * itself must pass, both, or neither for every query. A reader with no query
59
+ * to ask the predicate of takes it as reaching every query.
60
+ */
57
61
  export type SyncInvalidationFilters = {
58
62
  queryKey?: readonly unknown[];
63
+ predicate?: (query: {
64
+ queryKey: readonly unknown[];
65
+ }) => boolean;
59
66
  };
60
67
  /**
61
68
  * The invalidations sync has asked of `queryClient` for changes after
@@ -247,13 +247,14 @@ function logSyncInvalidations(queryClient, events, filters) {
247
247
  const after = local ? "local" : (cursorForEvents(events) ?? "local");
248
248
  const log = syncInvalidationLog(queryClient);
249
249
  for (const requested of filters) {
250
- // A predicate decides by the query itself, so it is kept as reaching
251
- // every query.
250
+ // A predicate decides by the query itself, so it is kept for the reader
251
+ // to ask of the query it holds.
252
252
  log.entries.push({
253
253
  after,
254
- filters: requested.queryKey && !requested.predicate
255
- ? { queryKey: requested.queryKey }
256
- : {},
254
+ filters: {
255
+ ...(requested.queryKey ? { queryKey: requested.queryKey } : {}),
256
+ ...(requested.predicate ? { predicate: requested.predicate } : {}),
257
+ },
257
258
  });
258
259
  }
259
260
  while (log.entries.length > SYNC_INVALIDATION_LOG_LIMIT) {
@@ -26,9 +26,9 @@ export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import
26
26
  ok: boolean;
27
27
  }>>;
28
28
  export declare const postCollabText: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
29
- text?: undefined;
30
29
  ok?: undefined;
31
30
  error: string;
31
+ text?: undefined;
32
32
  } | {
33
33
  error?: undefined;
34
34
  ok: boolean;
@@ -13,9 +13,9 @@ export declare const postCollabPatch: import("h3").EventHandlerWithFetch<import(
13
13
  ok: boolean;
14
14
  }>>;
15
15
  export declare const getCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
+ data?: undefined;
16
17
  error: string;
17
18
  docId?: undefined;
18
- data?: undefined;
19
19
  } | {
20
20
  error?: undefined;
21
21
  docId: string;
@@ -38,8 +38,9 @@ export const SCHEMA_SNAPSHOT = {
38
38
  'CREATE TABLE "agent_native_embed_session_revocations" (\n "owner_hash" text NOT NULL,\n "revoked_before" bigint NOT NULL\n);',
39
39
  'CREATE TABLE "agent_native_embed_tickets" (\n "ticket_hash" text NOT NULL,\n "owner_email" text NOT NULL,\n "org_id" text,\n "target_path" text NOT NULL,\n "scope" text,\n "created_at" bigint NOT NULL,\n "expires_at" bigint NOT NULL,\n "consumed_at" bigint,\n "renewal_expires_at" bigint,\n "session_active_until" bigint,\n "audience_host" text,\n "audience_base_path" text\n);',
40
40
  'CREATE TABLE "agent_resource_versions" (\n "id" text NOT NULL,\n "resource_type" text NOT NULL,\n "resource_id" text NOT NULL,\n "version_number" bigint NOT NULL,\n "created_at" text NOT NULL,\n "created_by" text,\n "actor_kind" text DEFAULT \'human\'::text NOT NULL,\n "owner_email" text,\n "org_id" text,\n "visibility" text DEFAULT \'private\'::text NOT NULL,\n "title" text,\n "summary" text,\n "snapshot_json" text NOT NULL,\n "metadata_json" text\n);',
41
+ 'CREATE TABLE "agent_review_comment_links" (\n "provider" text NOT NULL,\n "external_kind" text NOT NULL,\n "external_id" text NOT NULL,\n "comment_id" text NOT NULL,\n "resource_type" text NOT NULL,\n "resource_id" text NOT NULL,\n "created_at" text NOT NULL\n);',
41
42
  'CREATE TABLE "agent_review_comment_reactions" (\n "comment_id" text NOT NULL,\n "actor_email" text NOT NULL,\n "reaction" text NOT NULL,\n "created_at" text NOT NULL\n);',
42
- 'CREATE TABLE "agent_review_comments" (\n "id" text NOT NULL,\n "resource_type" text NOT NULL,\n "resource_id" text NOT NULL,\n "thread_id" text NOT NULL,\n "parent_comment_id" text,\n "target_id" text,\n "kind" text DEFAULT \'comment\'::text NOT NULL,\n "status" text DEFAULT \'open\'::text NOT NULL,\n "anchor_json" text,\n "body" text NOT NULL,\n "author_email" text,\n "author_name" text,\n "created_by" text DEFAULT \'human\'::text NOT NULL,\n "resolution_target" text,\n "reply_route_target" text DEFAULT \'legacy\'::text,\n "notification_completed_at" text,\n "mentions_json" text,\n "owner_email" text,\n "org_id" text,\n "visibility" text DEFAULT \'private\'::text NOT NULL,\n "resolved_by" text,\n "resolved_at" text,\n "consumed_at" text,\n "deleted_by" text,\n "deleted_at" text,\n "created_at" text NOT NULL,\n "updated_at" text NOT NULL,\n "metadata_json" text\n);',
43
+ 'CREATE TABLE "agent_review_comments" (\n "id" text NOT NULL,\n "resource_type" text NOT NULL,\n "resource_id" text NOT NULL,\n "thread_id" text NOT NULL,\n "parent_comment_id" text,\n "target_id" text,\n "kind" text DEFAULT \'comment\'::text NOT NULL,\n "status" text DEFAULT \'open\'::text NOT NULL,\n "anchor_json" text,\n "body" text NOT NULL,\n "author_email" text,\n "author_name" text,\n "created_by" text DEFAULT \'human\'::text NOT NULL,\n "resolution_target" text,\n "reply_route_target" text DEFAULT \'legacy\'::text,\n "notification_completed_at" text,\n "mentions_json" text,\n "owner_email" text,\n "org_id" text,\n "visibility" text DEFAULT \'private\'::text NOT NULL,\n "resolved_by" text,\n "resolved_at" text,\n "consumed_at" text,\n "deleted_by" text,\n "deleted_at" text,\n "created_at" text NOT NULL,\n "updated_at" text NOT NULL,\n "metadata_json" text,\n "surface_id" text,\n "anchor_kind" text,\n "anchor_version" integer,\n "revision" integer DEFAULT 1 NOT NULL\n);',
43
44
  'CREATE TABLE "agent_review_notification_deliveries" (\n "comment_id" text NOT NULL,\n "recipient_email" text NOT NULL,\n "claim_token" text NOT NULL,\n "claimed_at" text NOT NULL,\n "sent_at" text\n);',
44
45
  'CREATE TABLE "agent_review_statuses" (\n "id" text NOT NULL,\n "resource_type" text NOT NULL,\n "resource_id" text NOT NULL,\n "status" text NOT NULL,\n "note" text,\n "updated_by" text,\n "updated_at" text NOT NULL,\n "owner_email" text,\n "org_id" text,\n "visibility" text DEFAULT \'private\'::text NOT NULL,\n "metadata_json" text\n);',
45
46
  'CREATE TABLE "agent_review_suggestion_amendments" (\n "idempotency_key" text NOT NULL,\n "suggestion_id" text NOT NULL,\n "revision" integer NOT NULL,\n "author_email" text NOT NULL,\n "owner_email" text,\n "org_id" text,\n "visibility" text DEFAULT \'private\'::text NOT NULL,\n "request_json" text NOT NULL,\n "before_json" text NOT NULL,\n "after_json" text NOT NULL,\n "created_at" text NOT NULL\n);',
@@ -212,6 +213,7 @@ export const SCHEMA_SNAPSHOT = {
212
213
  'ALTER TABLE "agent_native_embed_session_revocations" ADD CONSTRAINT "agent_native_embed_session_revocations_pkey" PRIMARY KEY (owner_hash);',
213
214
  'ALTER TABLE "agent_native_embed_tickets" ADD CONSTRAINT "agent_native_embed_tickets_pkey" PRIMARY KEY (ticket_hash);',
214
215
  'ALTER TABLE "agent_resource_versions" ADD CONSTRAINT "agent_resource_versions_pkey" PRIMARY KEY (id);',
216
+ 'ALTER TABLE "agent_review_comment_links" ADD CONSTRAINT "agent_review_comment_links_pkey" PRIMARY KEY (provider, external_kind, external_id);',
215
217
  'ALTER TABLE "agent_review_comment_reactions" ADD CONSTRAINT "agent_review_comment_reactions_pkey" PRIMARY KEY (comment_id, actor_email, reaction);',
216
218
  'ALTER TABLE "agent_review_comments" ADD CONSTRAINT "agent_review_comments_pkey" PRIMARY KEY (id);',
217
219
  'ALTER TABLE "agent_review_notification_deliveries" ADD CONSTRAINT "agent_review_notification_deliveries_pkey" PRIMARY KEY (comment_id, recipient_email);',
@@ -432,6 +434,7 @@ export const SCHEMA_SNAPSHOT = {
432
434
  "CREATE INDEX idx_agent_resource_versions_org ON public.agent_resource_versions USING btree (org_id, created_at);",
433
435
  "CREATE INDEX idx_agent_resource_versions_owner ON public.agent_resource_versions USING btree (owner_email, created_at);",
434
436
  "CREATE UNIQUE INDEX idx_agent_resource_versions_resource_number ON public.agent_resource_versions USING btree (resource_type, resource_id, version_number);",
437
+ "CREATE INDEX idx_agent_review_comment_links_comment ON public.agent_review_comment_links USING btree (comment_id);",
435
438
  "CREATE INDEX agent_review_comments_access_owner_lower_idx ON public.agent_review_comments USING btree (lower(owner_email));",
436
439
  "CREATE INDEX idx_agent_review_comments_org ON public.agent_review_comments USING btree (org_id, created_at);",
437
440
  "CREATE INDEX idx_agent_review_comments_owner ON public.agent_review_comments USING btree (owner_email, created_at);",
@@ -683,6 +686,7 @@ export const SCHEMA_SNAPSHOT = {
683
686
  "agent_native_embed_session_revocations",
684
687
  "agent_native_embed_tickets",
685
688
  "agent_resource_versions",
689
+ "agent_review_comment_links",
686
690
  "agent_review_comment_reactions",
687
691
  "agent_review_comments",
688
692
  "agent_review_notification_deliveries",
@@ -1179,6 +1183,7 @@ export const SCHEMA_SNAPSHOT = {
1179
1183
  ["resources", "visibility-expires-index", null],
1180
1184
  ["resources", "widen-bigint", null],
1181
1185
  ["review", "baseline", null],
1186
+ ["review", "storage-contract", "ed0e701a19ad1c15"],
1182
1187
  ["review_suggestions", "baseline", null],
1183
1188
  ["sandbox_executions", "baseline", null],
1184
1189
  [
@@ -33,9 +33,9 @@ declare const _default: import("../../action.js").ActionDefinition<{
33
33
  idempotencyKey: string;
34
34
  deleted: boolean;
35
35
  } | {
36
+ id?: undefined;
36
37
  error: string;
37
38
  url?: undefined;
38
- id?: undefined;
39
39
  provider?: undefined;
40
40
  configured?: undefined;
41
41
  } | {
@@ -45,8 +45,8 @@ declare const _default: import("../../action.js").ActionDefinition<{
45
45
  provider: string;
46
46
  configured?: undefined;
47
47
  } | {
48
- url?: undefined;
49
48
  id?: undefined;
49
+ url?: undefined;
50
50
  provider?: undefined;
51
51
  error: string;
52
52
  configured: boolean;
@@ -2,7 +2,7 @@ export type { FileUploadDeleteInput, FileUploadInput, FileUploadProvider, FileUp
2
2
  export { registerFileUploadProvider, unregisterFileUploadProvider, listFileUploadProviders, getActiveFileUploadProvider, getActiveFileUploadProviderForRequest, deleteUploadedFile, uploadFile, } from "./registry.js";
3
3
  export { builderFileUploadProvider } from "./builder.js";
4
4
  export { getBuilderUploadCredentialStatus } from "./status.js";
5
- export { ensureS3FileUploadProvider, s3FileUploadProvider } from "./s3.js";
5
+ export { ensureS3FileUploadProvider, S3RequestError, s3FileUploadProvider, } from "./s3.js";
6
6
  export { cancelS3ResponseBody, fetchS3WithTimeout, presignS3GetUrl, readS3ErrorText, rfc3986, s3ObjectUri, sha256Hex, signS3Request, signedS3Request, trustedDeploymentS3Origin, type PresignS3GetOptions, type S3ClientConfig, type S3Method, type SignedS3RequestOptions, } from "./s3-client.js";
7
7
  export { FILE_STORAGE_SECRET_KEYS, getFileStorageStatus, saveFileStorage, clearFileStorage, type FileStorageField, type FileStorageProviderId, type FileStorageStatus, type SaveFileStorageInput, } from "./storage-settings.js";
8
8
  export { preUploadImageAttachments, preUploadAttachments, isFileUploadProviderConfigured, type PreUploadAttachmentsResult, type PreUploadedImageAttachment, type PreUploadedFileAttachment, } from "./pre-upload-attachments.js";
@@ -1,7 +1,7 @@
1
1
  export { registerFileUploadProvider, unregisterFileUploadProvider, listFileUploadProviders, getActiveFileUploadProvider, getActiveFileUploadProviderForRequest, deleteUploadedFile, uploadFile, } from "./registry.js";
2
2
  export { builderFileUploadProvider } from "./builder.js";
3
3
  export { getBuilderUploadCredentialStatus } from "./status.js";
4
- export { ensureS3FileUploadProvider, s3FileUploadProvider } from "./s3.js";
4
+ export { ensureS3FileUploadProvider, S3RequestError, s3FileUploadProvider, } from "./s3.js";
5
5
  export { cancelS3ResponseBody, fetchS3WithTimeout, presignS3GetUrl, readS3ErrorText, rfc3986, s3ObjectUri, sha256Hex, signS3Request, signedS3Request, trustedDeploymentS3Origin, } from "./s3-client.js";
6
6
  export { FILE_STORAGE_SECRET_KEYS, getFileStorageStatus, saveFileStorage, clearFileStorage, } from "./storage-settings.js";
7
7
  export { preUploadImageAttachments, preUploadAttachments, isFileUploadProviderConfigured, } from "./pre-upload-attachments.js";
@@ -1,3 +1,12 @@
1
1
  import type { FileUploadProvider } from "./types.js";
2
+ /**
3
+ * A request the bucket answered with an error. `status` lets callers tell a
4
+ * refused bucket (401, 403, or 404 for a missing bucket) from an outage.
5
+ */
6
+ export declare class S3RequestError extends Error {
7
+ readonly provider = "s3";
8
+ readonly status: number;
9
+ constructor(message: string, status: number);
10
+ }
2
11
  export declare const s3FileUploadProvider: FileUploadProvider;
3
12
  export declare function ensureS3FileUploadProvider(): void;
@@ -103,6 +103,19 @@ async function readRequestConfig() {
103
103
  await prefetchSecrets(STORAGE_SECRET_KEYS);
104
104
  return (await readSavedConfig()) ?? readEnvConfig();
105
105
  }
106
+ /**
107
+ * A request the bucket answered with an error. `status` lets callers tell a
108
+ * refused bucket (401, 403, or 404 for a missing bucket) from an outage.
109
+ */
110
+ export class S3RequestError extends Error {
111
+ provider = "s3";
112
+ status;
113
+ constructor(message, status) {
114
+ super(message);
115
+ this.name = "S3RequestError";
116
+ this.status = status;
117
+ }
118
+ }
106
119
  const PUT_TIMEOUT_MS = 120_000;
107
120
  const DELETE_TIMEOUT_MS = 30_000;
108
121
  function encodeKey(key) {
@@ -117,7 +130,7 @@ async function putObject(config, key, data, contentType) {
117
130
  });
118
131
  if (!response.ok) {
119
132
  const detail = await readS3ErrorText(response);
120
- throw new Error(`S3 PutObject failed (${response.status}): ${detail || response.statusText}`);
133
+ throw new S3RequestError(`S3 PutObject failed (${response.status}): ${detail || response.statusText}`, response.status);
121
134
  }
122
135
  await cancelS3ResponseBody(response);
123
136
  return `${config.publicBaseUrl}/${encodeKey(key)}`;
@@ -1,17 +1,17 @@
1
1
  {
2
- "@agent-native/agentkit": "0.227.0",
2
+ "@agent-native/agentkit": "0.228.0-nightly-20261110212400",
3
3
  "@agent-native/code-agents-ui": "0.1.75",
4
- "@agent-native/core": "0.227.0",
5
- "@agent-native/core-corpus": "0.227.0",
4
+ "@agent-native/core": "0.228.0-nightly-20261110212400",
5
+ "@agent-native/core-corpus": "0.228.0-nightly-20261110212400",
6
6
  "@agent-native/creative-context": "0.8.69",
7
- "@agent-native/dispatch": "0.42.4",
7
+ "@agent-native/dispatch": "0.42.5-nightly-20261110212400",
8
8
  "@agent-native/embedding": "0.2.0",
9
9
  "@agent-native/migrate": "0.0.7",
10
10
  "@agent-native/otel": "0.1.36",
11
11
  "@agent-native/pinpoint": "0.1.112",
12
12
  "@agent-native/recap-cli": "0.5.100",
13
- "@agent-native/scheduling": "0.2.56",
13
+ "@agent-native/scheduling": "0.2.57-nightly-20261110212400",
14
14
  "@agent-native/shared-app-config": "0.1.2",
15
- "@agent-native/skills": "0.4.4",
16
- "@agent-native/toolkit": "0.227.0"
15
+ "@agent-native/skills": "0.4.5-nightly-20261110212400",
16
+ "@agent-native/toolkit": "0.228.0-nightly-20261110212400"
17
17
  }
@@ -28,10 +28,6 @@ const MAX_PAUSED_RECHECKS_PER_TICK = 8;
28
28
  const PAUSED_RECHECK_MS = 15 * 60_000;
29
29
  const _activeScheduledJobs = new Set();
30
30
  const _preflightingScheduledJobs = new Set();
31
- let _hasJobsCache;
32
- let _lastJobsCheck = 0;
33
- const JOBS_CHECK_INTERVAL_MS = 5 * 60_000;
34
- let _emitterSubscribed = false;
35
31
  async function recordSchedulerHealthForScopes(input) {
36
32
  const orgIds = [...new Set(input.orgIds)];
37
33
  const scopes = orgIds.length > 0 ? orgIds : [null];
@@ -49,22 +45,6 @@ async function recordSchedulerHealthForScopes(input) {
49
45
  }
50
46
  }
51
47
  }
52
- function subscribeToJobsResourceEvents() {
53
- if (_emitterSubscribed)
54
- return;
55
- _emitterSubscribed = true;
56
- import("../resources/emitter.js")
57
- .then(({ getResourcesEmitter }) => {
58
- getResourcesEmitter().on("resources", (event) => {
59
- if (typeof event?.path === "string" && event.path.startsWith("jobs/")) {
60
- _hasJobsCache = undefined;
61
- }
62
- });
63
- })
64
- .catch((err) => {
65
- console.warn("[jobs] resource-event subscription failed:", err instanceof Error ? err.message : err);
66
- });
67
- }
68
48
  export async function processRecurringJobs(deps) {
69
49
  const leaseOwner = await acquireAutomationSchedulerLease({
70
50
  appId: deps.appId,
@@ -122,7 +102,6 @@ export async function processRecurringJobs(deps) {
122
102
  throw releaseErrorToThrow;
123
103
  }
124
104
  async function processRecurringJobsWithLease(deps, leaseSignal, assertCanStart) {
125
- subscribeToJobsResourceEvents();
126
105
  try {
127
106
  const { runUploadReceiptCleanupOnce } = await import("../file-upload/actions/upload-image.js");
128
107
  await runUploadReceiptCleanupOnce();
@@ -147,26 +126,18 @@ async function processRecurringJobsWithLease(deps, leaseSignal, assertCanStart)
147
126
  orgIds: [],
148
127
  checkedAt: nowMs,
149
128
  });
150
- if (_hasJobsCache === false &&
151
- nowMs - _lastJobsCheck < JOBS_CHECK_INTERVAL_MS) {
152
- await recordSchedulerHealthForScopes({
153
- appId: deps.appId,
154
- orgIds: [],
155
- checkedAt: nowMs,
156
- });
157
- return;
158
- }
159
129
  const reservedJobKeys = new Set();
160
130
  const startedJobKeys = new Set();
161
131
  const healthOrgIds = new Set();
162
132
  let healthError = null;
163
133
  let dispatchedAt;
164
134
  try {
135
+ // Read jobs/ on every tick. Any instance can create an automation, so a
136
+ // remembered "no jobs" here would hold its first run back until it lapsed.
165
137
  const jobResources = await resourceListAllOwners("jobs/");
166
- _hasJobsCache = jobResources.some((r) => r.path.endsWith(".md") && !r.path.endsWith(".keep"));
167
- _lastJobsCheck = nowMs;
168
- if (!_hasJobsCache)
138
+ if (!jobResources.some((r) => r.path.endsWith(".md") && !r.path.endsWith(".keep"))) {
169
139
  return;
140
+ }
170
141
  const now = new Date();
171
142
  const dueJobCandidates = [];
172
143
  const pausedRechecks = [];
@@ -439,8 +410,6 @@ async function processRecurringJobsWithLease(deps, leaseSignal, assertCanStart)
439
410
  const { isConnectionError } = await import("../db/client.js");
440
411
  if (isConnectionError(err)) {
441
412
  healthError = "The scheduler could not reach the database.";
442
- _hasJobsCache = undefined;
443
- _lastJobsCheck = 0;
444
413
  return;
445
414
  }
446
415
  const detail = err instanceof Error
@@ -0,0 +1,16 @@
1
+ import { type BrowserTabTarget, type BrowserTabTargetSlot } from "../server/request-context.js";
2
+ /**
3
+ * An MCP tool call comes from no browser tab, so tab-scoped app state
4
+ * (navigation, navigate commands, selection) would otherwise fall back to
5
+ * account-wide rows no tab keeps current. This slot points the call at the
6
+ * tab of this app the user used last. The lookup runs only when the call
7
+ * first reads or writes tab-scoped state.
8
+ */
9
+ export declare function createBrowserTabTargetSlot(): BrowserTabTargetSlot;
10
+ /**
11
+ * Tells the calling agent which tab a call's tab-scoped reads and writes
12
+ * reached, since nothing in the call itself says so.
13
+ */
14
+ export declare function browserTabTargetNote(target: BrowserTabTarget, now?: number): string;
15
+ /** Adds the tab note to a successful tool result that used tab-scoped state. */
16
+ export declare function withBrowserTabTargetNote<T>(result: T, slot: BrowserTabTargetSlot | undefined): T;
@@ -0,0 +1,58 @@
1
+ import { findLastUsedBrowserTab, LAST_USED_BROWSER_TAB_MAX_AGE_MS, } from "../application-state/last-used-tab.js";
2
+ import { getRequestUserEmail, } from "../server/request-context.js";
3
+ /**
4
+ * An MCP tool call comes from no browser tab, so tab-scoped app state
5
+ * (navigation, navigate commands, selection) would otherwise fall back to
6
+ * account-wide rows no tab keeps current. This slot points the call at the
7
+ * tab of this app the user used last. The lookup runs only when the call
8
+ * first reads or writes tab-scoped state.
9
+ */
10
+ export function createBrowserTabTargetSlot() {
11
+ let pending;
12
+ const slot = {
13
+ resolve() {
14
+ pending ??= (async () => {
15
+ const userEmail = getRequestUserEmail();
16
+ const target = userEmail
17
+ ? await findLastUsedBrowserTab(userEmail)
18
+ : { kind: "unavailable" };
19
+ slot.resolved = target;
20
+ return target;
21
+ })();
22
+ return pending;
23
+ },
24
+ };
25
+ return slot;
26
+ }
27
+ function usedAgo(usedAt, now) {
28
+ const minutes = Math.floor(Math.max(0, now - usedAt) / 60_000);
29
+ if (minutes < 1)
30
+ return "less than a minute ago";
31
+ if (minutes < 60)
32
+ return `${minutes} min ago`;
33
+ return `${Math.floor(minutes / 60)} h ago`;
34
+ }
35
+ /**
36
+ * Tells the calling agent which tab a call's tab-scoped reads and writes
37
+ * reached, since nothing in the call itself says so.
38
+ */
39
+ export function browserTabTargetNote(target, now = Date.now()) {
40
+ if (target.kind === "last-used") {
41
+ return `Browser tab: this call used the user's most recently used tab of this app (last used ${usedAgo(target.usedAt, now)}). A navigate command reaches that tab only while it stays open, and this result doesn't confirm that it moved.`;
42
+ }
43
+ const hours = Math.round(LAST_USED_BROWSER_TAB_MAX_AGE_MS / 3_600_000);
44
+ return `Browser tab: no tab of this app was used in the last ${hours} hours, so there was no on-screen state to read and a navigate command won't move any tab. Ask the user to open the app in a browser tab, then retry.`;
45
+ }
46
+ /** Adds the tab note to a successful tool result that used tab-scoped state. */
47
+ export function withBrowserTabTargetNote(result, slot) {
48
+ const target = slot?.resolved;
49
+ if (!target || !result || typeof result !== "object")
50
+ return result;
51
+ const { content, isError } = result;
52
+ if (isError === true || !Array.isArray(content))
53
+ return result;
54
+ return {
55
+ ...result,
56
+ content: [...content, { type: "text", text: browserTabTargetNote(target) }],
57
+ };
58
+ }
@@ -24,6 +24,7 @@ import { mcpApprovalPrompt, mcpApprovalRelativeLinkNote, mcpApprovalReviewUrl, }
24
24
  import { mcpApprovalAppId } from "./approval-store.js";
25
25
  export { isAutoReadExcludedActionName } from "./action-eligibility.js";
26
26
  import { isMcpAppsInlineEnabled } from "./app-cards-lab.js";
27
+ import { createBrowserTabTargetSlot, withBrowserTabTargetNote, } from "./browser-tab-target.js";
27
28
  import { getBuiltinCrossAppTools } from "./builtin-tools.js";
28
29
  import { MCP_CONNECT_OAUTH_CLIENT_ID, MCP_CONNECT_SCOPE, } from "./connect-store.js";
29
30
  import { createDirectActionCall } from "./direct-action-call.js";
@@ -2159,7 +2160,7 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
2159
2160
  callerAuthUserId ??= import("../server/better-auth-instance.js").then(({ findBetterAuthUserIdForEmail }) => findBetterAuthUserIdForEmail(email));
2160
2161
  return callerAuthUserId;
2161
2162
  }
2162
- async function withCallerContext(fn, mcpRequestId, authUserId) {
2163
+ async function withCallerContext(fn, mcpRequestId, authUserId, run) {
2163
2164
  const orgId = await orgIdPromise;
2164
2165
  return runWithRequestContext({
2165
2166
  userEmail: effectiveIdentity?.userEmail,
@@ -2178,10 +2179,16 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
2178
2179
  : {}),
2179
2180
  ...(mcpRequestId ? { mcpRequestId } : {}),
2180
2181
  mcpCredentialIssuedAtMs: effectiveIdentity?.mcpCredentialIssuedAtMs ?? null,
2182
+ ...(run ? { run } : {}),
2181
2183
  }, fn);
2182
2184
  }
2183
2185
  async function withToolCallContext(fn, mcpRequestId) {
2184
- return withCallerContext(fn, mcpRequestId, await resolveCallerAuthUserId());
2186
+ // Only a signed-in person has browser tabs for a call to act on.
2187
+ const browserTabTarget = hasVerifiedMcpUserIdentity(effectiveIdentity)
2188
+ ? createBrowserTabTargetSlot()
2189
+ : undefined;
2190
+ const result = await withCallerContext(fn, mcpRequestId, await resolveCallerAuthUserId(), browserTabTarget ? { browserTabTarget } : undefined);
2191
+ return withBrowserTabTargetNote(result, browserTabTarget);
2185
2192
  }
2186
2193
  const actionCallContext = {
2187
2194
  appId: config.appId,
@@ -1,12 +1,12 @@
1
1
  export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
2
+ error?: undefined;
2
3
  count: number;
3
4
  updated?: undefined;
4
- error?: undefined;
5
5
  ok?: undefined;
6
6
  } | {
7
+ error?: undefined;
7
8
  count?: undefined;
8
9
  updated: number;
9
- error?: undefined;
10
10
  ok?: undefined;
11
11
  } | {
12
12
  count?: undefined;
@@ -14,8 +14,8 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
14
14
  error: string;
15
15
  ok?: undefined;
16
16
  } | {
17
+ error?: undefined;
17
18
  count?: undefined;
18
19
  updated?: undefined;
19
- error?: undefined;
20
20
  ok: boolean;
21
21
  }>>;
@@ -90,9 +90,9 @@ export declare const setWorkspaceAppDefaultVisibilityHandler: import("h3").Event
90
90
  visibility: WorkspaceAppDefaultVisibility;
91
91
  }>>;
92
92
  export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
93
+ name?: undefined;
93
94
  success: boolean;
94
95
  id?: undefined;
95
- name?: undefined;
96
96
  role?: undefined;
97
97
  } | {
98
98
  success?: undefined;
@@ -1,6 +1,6 @@
1
1
  export declare function createProgressHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").AgentRun | import("./types.js").AgentRun[] | {
2
- error: string;
3
2
  ok?: undefined;
3
+ error: string;
4
4
  } | {
5
5
  error?: undefined;
6
6
  ok: boolean;
@@ -76,7 +76,10 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
78
  id?: undefined;
79
+ deleted?: undefined;
80
+ provider?: undefined;
79
81
  message?: undefined;
82
+ found?: undefined;
80
83
  providers: {
81
84
  id: string;
82
85
  label: string;
@@ -87,47 +90,44 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
87
90
  updatedAt: number;
88
91
  }[];
89
92
  count: number;
90
- provider?: undefined;
91
- found?: undefined;
92
- deleted?: undefined;
93
93
  registered?: undefined;
94
94
  label?: undefined;
95
95
  } | {
96
96
  id?: undefined;
97
+ deleted?: undefined;
97
98
  count?: undefined;
98
99
  message?: undefined;
99
100
  providers?: undefined;
100
101
  found: boolean;
101
102
  provider: import("../custom-registry.js").CustomProviderConfig;
102
- deleted?: undefined;
103
103
  registered?: undefined;
104
104
  label?: undefined;
105
105
  } | {
106
+ deleted?: undefined;
107
+ provider?: undefined;
106
108
  count?: undefined;
107
109
  message?: undefined;
108
110
  providers?: undefined;
109
- provider?: undefined;
110
111
  found: boolean;
111
112
  id: string;
112
- deleted?: undefined;
113
113
  registered?: undefined;
114
114
  label?: undefined;
115
115
  } | {
116
+ provider?: undefined;
116
117
  count?: undefined;
117
118
  message?: undefined;
118
- providers?: undefined;
119
- provider?: undefined;
120
119
  found?: undefined;
120
+ providers?: undefined;
121
121
  deleted: boolean;
122
122
  id: string;
123
123
  registered?: undefined;
124
124
  label?: undefined;
125
125
  } | {
126
- count?: undefined;
127
- providers?: undefined;
126
+ deleted?: undefined;
128
127
  provider?: undefined;
128
+ count?: undefined;
129
129
  found?: undefined;
130
- deleted?: undefined;
130
+ providers?: undefined;
131
131
  registered: boolean;
132
132
  id: string;
133
133
  label: string;