@agent-native/core 0.213.1 → 0.216.0

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 (169) hide show
  1. package/dist/action.js +54 -1
  2. package/dist/agent/actions/disconnect-chatgpt-subscription.js +5 -0
  3. package/dist/agent/actions/get-agentkit-capabilities.js +3 -3
  4. package/dist/agent/actions/manage-builder-connection.js +5 -0
  5. package/dist/agent/actions/manage-provider-key-policy.js +5 -0
  6. package/dist/agent/actions/manage-provider-models.js +5 -0
  7. package/dist/agent/actions/manage-service-providers.js +5 -0
  8. package/dist/agent/actions/select-chatgpt-subscription-account.js +5 -0
  9. package/dist/agent/context-xray/actions/context-evict.js +5 -0
  10. package/dist/agent/context-xray/actions/context-pin.js +5 -0
  11. package/dist/agent/context-xray/actions/context-report.js +5 -0
  12. package/dist/agent/context-xray/actions/context-restore.js +5 -0
  13. package/dist/agent/execution-fence.d.ts +1 -0
  14. package/dist/agent/execution-fence.js +5 -0
  15. package/dist/agent/production-agent.d.ts +35 -2
  16. package/dist/agent/production-agent.js +629 -32
  17. package/dist/agent/run-manager.d.ts +2 -0
  18. package/dist/agent/run-manager.js +397 -115
  19. package/dist/agent/run-slot-lock.d.ts +2 -0
  20. package/dist/agent/run-slot-lock.js +6 -0
  21. package/dist/agent/run-store.d.ts +5 -0
  22. package/dist/agent/run-store.js +423 -156
  23. package/dist/agent/thread-data-builder.js +27 -5
  24. package/dist/agent/types.d.ts +6 -12
  25. package/dist/agentkit/capabilities.d.ts +1 -1
  26. package/dist/appearance/actions/get-theme-family.d.ts +8 -0
  27. package/dist/appearance/actions/get-theme-family.js +38 -0
  28. package/dist/appearance/actions/set-theme-family.d.ts +7 -0
  29. package/dist/appearance/actions/set-theme-family.js +33 -0
  30. package/dist/appearance/theme-family.d.ts +13 -0
  31. package/dist/appearance/theme-family.js +38 -0
  32. package/dist/application-state/store.d.ts +5 -0
  33. package/dist/application-state/store.js +26 -0
  34. package/dist/chat-threads/schema-migrations.js +17 -0
  35. package/dist/chat-threads/store.d.ts +21 -1
  36. package/dist/chat-threads/store.js +223 -41
  37. package/dist/cli/skills-content/turn-into-app-skill.d.ts +1 -1
  38. package/dist/cli/skills-content/turn-into-app-skill.js +20 -18
  39. package/dist/cli/templates-meta.js +14 -0
  40. package/dist/client/analytics.js +167 -17
  41. package/dist/client/chat/agentkit-agent-native.js +79 -3
  42. package/dist/client/chat/agentkit-protocol.js +80 -42
  43. package/dist/client/chat/runtime.d.ts +5 -1
  44. package/dist/client/chat/runtime.js +46 -3
  45. package/dist/client/consent.d.ts +36 -0
  46. package/dist/client/consent.js +283 -0
  47. package/dist/client/review/use-review.d.ts +2 -0
  48. package/dist/client/review/use-review.js +28 -1
  49. package/dist/client/sharing/useResourceAccessGate.d.ts +3 -2
  50. package/dist/client/sharing/useResourceAccessGate.js +9 -1
  51. package/dist/client/theme-family.d.ts +35 -0
  52. package/dist/client/theme-family.js +223 -0
  53. package/dist/client/theme.d.ts +1 -1
  54. package/dist/client/theme.js +5 -2
  55. package/dist/client/ui/index.d.ts +1 -0
  56. package/dist/client/ui/index.js +1 -0
  57. package/dist/client/use-chat-thread-list.d.ts +2 -0
  58. package/dist/client/use-chat-thread-list.js +12 -2
  59. package/dist/client/use-chat-threads.d.ts +1 -0
  60. package/dist/client/use-chat-threads.js +2 -0
  61. package/dist/connections/catalog.d.ts +5 -5
  62. package/dist/connections/catalog.js +22 -3
  63. package/dist/data-widgets/index.d.ts +374 -5
  64. package/dist/data-widgets/index.js +242 -11
  65. package/dist/db/migrations.js +4 -1
  66. package/dist/db/schema-snapshot.generated.js +8 -1
  67. package/dist/feature-flags/actions/set-feature-flag.js +5 -0
  68. package/dist/feature-flags/index.d.ts +1 -1
  69. package/dist/feature-flags/index.js +1 -1
  70. package/dist/feature-flags/store.d.ts +12 -0
  71. package/dist/feature-flags/store.js +79 -7
  72. package/dist/file-upload/actions/manage-file-storage.js +5 -0
  73. package/dist/file-upload/actions/upload-image.js +5 -0
  74. package/dist/framework-tools.js +2 -0
  75. package/dist/history/actions/create-resource-version.js +5 -0
  76. package/dist/history/actions/restore-resource-version.js +5 -0
  77. package/dist/ingestion/index.d.ts +1 -1
  78. package/dist/ingestion/index.js +1 -1
  79. package/dist/ingestion/pptx.d.ts +11 -0
  80. package/dist/ingestion/pptx.js +81 -1
  81. package/dist/integrations/actions/manage-messaging-channel.js +5 -0
  82. package/dist/jobs/scheduler.js +17 -1
  83. package/dist/labs/actions/set-lab.js +5 -0
  84. package/dist/localization/actions/set-localization-preference.js +5 -0
  85. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  86. package/dist/observability/actions/save-observability-instruction-update.js +5 -0
  87. package/dist/observability/actions/save-observability-review-summary.js +5 -0
  88. package/dist/provider-api/actions/custom-provider-registration.d.ts +19 -19
  89. package/dist/provider-api/actions/provider-api.d.ts +10 -10
  90. package/dist/provider-api/index.js +9 -2
  91. package/dist/review/actions/consume-review-feedback.js +5 -0
  92. package/dist/review/actions/create-review-comment.js +5 -0
  93. package/dist/review/actions/delete-review-comment.js +5 -0
  94. package/dist/review/actions/react-to-review-comment.js +5 -0
  95. package/dist/review/actions/reply-review-comment.js +5 -0
  96. package/dist/review/actions/resolve-review-thread.js +5 -0
  97. package/dist/review/actions/send-review-thread-to-agent.js +5 -0
  98. package/dist/review/actions/set-review-status.js +5 -0
  99. package/dist/review/actions/set-review-thread-muted.js +5 -0
  100. package/dist/review/actions/set-review-thread-unread.js +5 -0
  101. package/dist/review/actions/set-review-threads-unread.js +5 -0
  102. package/dist/review/actions/update-review-comment-anchor.js +5 -0
  103. package/dist/review/actions/update-review-comment.js +5 -0
  104. package/dist/review/suggestions/actions.js +60 -21
  105. package/dist/review/suggestions/store.d.ts +2 -0
  106. package/dist/review/suggestions/store.js +28 -0
  107. package/dist/secrets/actions/delete-api-key.js +5 -0
  108. package/dist/secrets/routes.d.ts +10 -10
  109. package/dist/server/action-discovery.js +8 -0
  110. package/dist/server/agent-chat/context-tools.js +105 -2
  111. package/dist/server/agent-chat-plugin.d.ts +2 -0
  112. package/dist/server/agent-chat-plugin.js +242 -54
  113. package/dist/server/agent-engine-default-model-route.d.ts +1 -1
  114. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  115. package/dist/server/application-state-maintenance.d.ts +2 -0
  116. package/dist/server/application-state-maintenance.js +2 -0
  117. package/dist/server/attribution.d.ts +14 -0
  118. package/dist/server/attribution.js +62 -2
  119. package/dist/server/auth.js +7 -0
  120. package/dist/server/consent-route.d.ts +41 -0
  121. package/dist/server/consent-route.js +102 -0
  122. package/dist/server/first-touch-route.d.ts +39 -0
  123. package/dist/server/first-touch-route.js +115 -0
  124. package/dist/server/index.d.ts +1 -0
  125. package/dist/server/index.js +1 -0
  126. package/dist/server/queued-submission-inbox-drain.d.ts +9 -0
  127. package/dist/server/queued-submission-inbox-drain.js +12 -0
  128. package/dist/server/request-origin.d.ts +6 -0
  129. package/dist/server/request-origin.js +12 -0
  130. package/dist/settings/org-settings.d.ts +2 -1
  131. package/dist/settings/org-settings.js +4 -1
  132. package/dist/settings/store.d.ts +8 -0
  133. package/dist/settings/store.js +14 -5
  134. package/dist/shared/consent.d.ts +31 -0
  135. package/dist/shared/consent.js +145 -0
  136. package/dist/shared/first-touch-persistence.d.ts +31 -0
  137. package/dist/shared/first-touch-persistence.js +53 -0
  138. package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
  139. package/dist/sharing/actions/set-resource-visibility.js +5 -0
  140. package/dist/sharing/actions/share-resource.js +5 -0
  141. package/dist/sharing/actions/unshare-resource.js +5 -0
  142. package/dist/templates/chat/.agents/skills/actions/references/action-fields.md +1 -1
  143. package/dist/templates/default/.agents/skills/actions/references/action-fields.md +1 -1
  144. package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +36 -80
  145. package/dist/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +54 -16
  146. package/dist/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +50 -73
  147. package/dist/templates/headless/.agents/skills/actions/references/action-fields.md +1 -1
  148. package/dist/templates/workspace-core/.agents/skills/actions/references/action-fields.md +1 -1
  149. package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +20 -18
  150. package/dist/usage/actions/get-usage-metrics.js +27 -4
  151. package/dist/usage/actions/manage-usage-alert.js +5 -0
  152. package/dist/vite/client.js +8 -6
  153. package/dist/workspace-connections/actions/bulk-update-workspace-user-groups.js +5 -0
  154. package/dist/workspace-connections/actions/delete-workspace-user-group.js +5 -0
  155. package/dist/workspace-connections/actions/upsert-workspace-user-group.js +5 -0
  156. package/docs/content/actions-access-control.mdx +8 -8
  157. package/docs/content/actions-defining.mdx +9 -8
  158. package/docs/content/template-design-developers.mdx +9 -9
  159. package/docs/content/template-design-features.mdx +1 -3
  160. package/docs/content/turn-into-app.mdx +4 -0
  161. package/package.json +4 -3
  162. package/src/templates/chat/.agents/skills/actions/references/action-fields.md +1 -1
  163. package/src/templates/default/.agents/skills/actions/references/action-fields.md +1 -1
  164. package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +36 -80
  165. package/src/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +54 -16
  166. package/src/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +50 -73
  167. package/src/templates/headless/.agents/skills/actions/references/action-fields.md +1 -1
  168. package/src/templates/workspace-core/.agents/skills/actions/references/action-fields.md +1 -1
  169. package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +20 -18
@@ -1,4 +1,5 @@
1
1
  import { AgentKitRunSlotBusyError } from "@agent-native/agentkit/client";
2
+ import { parseAgentQueuedSubmissionReceipt, } from "@agent-native/agentkit/protocol";
2
3
  import { AUTO_CONTINUE_OF_RUN_METADATA_KEY, CONTINUE_OF_RUN_METADATA_KEY, } from "../../agent/auto-continue.js";
3
4
  import { RUN_NOT_STARTED_METADATA_KEY } from "../../shared/agent-chat-run-not-started.js";
4
5
  import { agentEngineStatusUrlForChatApi, requireAgentEngineConfiguredForDispatch, } from "../agent-engine-readiness.js";
@@ -261,6 +262,8 @@ function defaultHttpRuntimeRequest(input) {
261
262
  messages: input.turn.messages,
262
263
  attachments: input.turn.attachments,
263
264
  submissionPayload: input.turn.submissionPayload,
265
+ options: input.turn.options,
266
+ references: input.turn.references,
264
267
  tools: input.turn.tools,
265
268
  model: input.turn.model,
266
269
  reasoningEffort: input.turn.reasoningEffort,
@@ -476,6 +479,43 @@ export function createHttpAgentChatRuntime(options) {
476
479
  }
477
480
  throw withTurnId(error, turnId);
478
481
  }
482
+ if (response.status === 202) {
483
+ let queuedSubmission;
484
+ try {
485
+ if (!response.headers
486
+ .get("Content-Type")
487
+ ?.toLowerCase()
488
+ .includes("application/json")) {
489
+ throw new TypeError("Expected a JSON queued submission receipt.");
490
+ }
491
+ queuedSubmission = parseAgentQueuedSubmissionReceipt(await response.json());
492
+ const submission = asRecord(turn.submissionPayload);
493
+ const expectedThreadId = summary.threadId ?? sessionId;
494
+ if (queuedSubmission.message.threadId !== expectedThreadId ||
495
+ (typeof submission?.id === "string" &&
496
+ queuedSubmission.message.id !== submission.id)) {
497
+ throw new TypeError("The queued submission receipt does not match this session turn.");
498
+ }
499
+ }
500
+ catch (cause) {
501
+ cleanup();
502
+ throw withTurnId(Object.assign(new Error(cause instanceof Error
503
+ ? `Invalid queued submission response: ${cause.message}`
504
+ : "Invalid queued submission response.", { cause }), {
505
+ code: "invalid_queued_submission_response",
506
+ status: response.status,
507
+ retryable: true,
508
+ }), turnId);
509
+ }
510
+ cleanup();
511
+ forgetTurnContext(turnId, turn);
512
+ return {
513
+ id: turnId,
514
+ sessionId,
515
+ queuedSubmission,
516
+ events: (async function* () { })(),
517
+ };
518
+ }
479
519
  const runId = response.headers.get("X-Run-Id") ?? undefined;
480
520
  const events = (async function* () {
481
521
  let retryableFailure = false;
@@ -3230,6 +3270,8 @@ export function createAgentNativeChatRuntime(options = {}) {
3230
3270
  ...(turn.submissionPayload !== undefined
3231
3271
  ? { submissionPayload: turn.submissionPayload }
3232
3272
  : {}),
3273
+ ...(turn.references?.length ? { references: turn.references } : {}),
3274
+ ...(turn.options ? { options: turn.options } : {}),
3233
3275
  ...(turn.queuePromotion
3234
3276
  ? {
3235
3277
  queuedMessageId: turn.queuePromotion.messageId,
@@ -3298,6 +3340,7 @@ export function createAgentNativeChatRuntime(options = {}) {
3298
3340
  const turnToContinue = continuation.attachments?.length
3299
3341
  ? { ...previousTurn, attachments: continuation.attachments }
3300
3342
  : previousTurn;
3343
+ const { submissionPayload: _submissionPayload, ...continuationTurn } = turnToContinue ?? {};
3301
3344
  const approval = continuation.approval;
3302
3345
  const connection = continuation.connection;
3303
3346
  const messageStateKey = continuation.turnId ?? session.id;
@@ -3314,7 +3357,7 @@ export function createAgentNativeChatRuntime(options = {}) {
3314
3357
  if (messageState)
3315
3358
  messageState.message.connectionPending = false;
3316
3359
  return startTurn({
3317
- ...turnToContinue,
3360
+ ...continuationTurn,
3318
3361
  prompt: continuation.prompt ??
3319
3362
  `The ${connection.id} connection is now available. Continue the requested work.`,
3320
3363
  metadata: {
@@ -3334,7 +3377,7 @@ export function createAgentNativeChatRuntime(options = {}) {
3334
3377
  if (messageState)
3335
3378
  messageState.message.approvalPending = false;
3336
3379
  return startTurn({
3337
- ...turnToContinue,
3380
+ ...continuationTurn,
3338
3381
  prompt: continuation.prompt,
3339
3382
  metadata: {
3340
3383
  ...previousMetadata,
@@ -3376,7 +3419,7 @@ export function createAgentNativeChatRuntime(options = {}) {
3376
3419
  if (messageState)
3377
3420
  messageState.message.approvalPending = false;
3378
3421
  return startTurn({
3379
- ...turnToContinue,
3422
+ ...continuationTurn,
3380
3423
  prompt: continuation.prompt ??
3381
3424
  "Approved. Go ahead and run the requested action.",
3382
3425
  metadata: {
@@ -0,0 +1,36 @@
1
+ import { type ConsentCategory, type ConsentChoice, type ConsentRegion } from "../shared/consent.js";
2
+ export type { ConsentCategory, ConsentChoice, ConsentRegion };
3
+ /** The visitor's stored choice, or `null` before they've made one. */
4
+ export declare function getConsentChoice(): ConsentChoice | null;
5
+ /**
6
+ * Whether this visitor gets the banner: the server's answer from the
7
+ * request's country, else the browser's time zone. Asked once a session.
8
+ */
9
+ export declare function resolveConsentRegion(): Promise<ConsentRegion>;
10
+ /**
11
+ * Whether `category` is allowed right now, on the time zone's guess until the
12
+ * server answers. Pair it with `whenConsentAllows` and a `revoke` that undoes
13
+ * whatever this let through.
14
+ */
15
+ export declare function consentAllowsNow(category: ConsentCategory): boolean;
16
+ export interface WhenConsentAllowsOptions {
17
+ /**
18
+ * Run now on the time zone's guess instead of waiting for the server, and
19
+ * call `revoke` if the server or the visitor then says no. Only for storage
20
+ * `revoke` can fully undo; a third-party script can't be unloaded.
21
+ */
22
+ optimistic?: boolean;
23
+ revoke?: () => void;
24
+ }
25
+ /**
26
+ * Run `run` once `category` is allowed: at once outside the consent region,
27
+ * and inside it when the visitor accepts. A later "reject" calls `revoke`.
28
+ */
29
+ export declare function whenConsentAllows(category: ConsentCategory, run: () => void, options?: WhenConsentAllowsOptions): void;
30
+ /** Store the visitor's choice for every app on this site, and apply it. */
31
+ export declare function setConsentChoice(choice: ConsentChoice): void;
32
+ /** Reopen the banner so the visitor can change their choice. */
33
+ export declare function openConsentSettings(): void;
34
+ /** Whether the banner should show now, for the banner component. */
35
+ export declare function shouldShowConsentBanner(): boolean;
36
+ export declare function subscribeConsent(listener: () => void): () => void;
@@ -0,0 +1,283 @@
1
+ import { CONSENT_COOKIE_LIFETIME_SECONDS, CONSENT_COOKIE_NAME, CONSENT_PATH, CONSENT_REGION_PATH, consentCookieDomain, isConsentRegionTimeZone, parseConsent, serializeConsent, } from "../shared/consent.js";
2
+ import { agentNativePath } from "./api-path.js";
3
+ import { agentNativeApiDisabledReason } from "./api-surface.js";
4
+ /** Strictly necessary for the banner: which region this session is in. */
5
+ const REGION_STORAGE_KEY = "an_consent_region";
6
+ /**
7
+ * Scripts that run before this module loads, like the Google tags the server
8
+ * injects into the page, queue `[category, run]` here.
9
+ */
10
+ const SCRIPT_QUEUE_KEY = "__AN_CONSENT_QUEUE__";
11
+ // One state per page, however many copies of this module a bundle carries.
12
+ const STATE_KEY = Symbol.for("agent-native.client.consent");
13
+ function state() {
14
+ const holder = globalThis;
15
+ return (holder[STATE_KEY] ??= {
16
+ region: null,
17
+ regionPromise: null,
18
+ waiters: [],
19
+ listeners: new Set(),
20
+ settingsOpen: false,
21
+ });
22
+ }
23
+ function readCookie(name) {
24
+ if (typeof document === "undefined")
25
+ return null;
26
+ try {
27
+ for (const part of document.cookie ? document.cookie.split(";") : []) {
28
+ const eq = part.indexOf("=");
29
+ if (eq !== -1 && part.slice(0, eq).trim() === name) {
30
+ return part.slice(eq + 1).trim();
31
+ }
32
+ }
33
+ }
34
+ catch {
35
+ // coercion-ok: a sandboxed iframe can't read cookies, so it holds no
36
+ // choice and the visitor is asked.
37
+ }
38
+ return null;
39
+ }
40
+ /** The visitor's stored choice, or `null` before they've made one. */
41
+ export function getConsentChoice() {
42
+ return parseConsent(readCookie(CONSENT_COOKIE_NAME));
43
+ }
44
+ function cookieDomain() {
45
+ const host = typeof window === "undefined" ? "" : window.location.hostname;
46
+ const domain = consentCookieDomain(host);
47
+ return domain ? `; domain=${domain}` : "";
48
+ }
49
+ function writeChoiceCookie(choice) {
50
+ try {
51
+ document.cookie =
52
+ `${CONSENT_COOKIE_NAME}=${serializeConsent(choice)}; path=/; ` +
53
+ `max-age=${CONSENT_COOKIE_LIFETIME_SECONDS}; SameSite=Lax${cookieDomain()}`;
54
+ }
55
+ catch {
56
+ // coercion-ok: a sandboxed iframe can't store the choice; it still
57
+ // applies to this page.
58
+ }
59
+ }
60
+ let serverChoiceWrites = Promise.resolve();
61
+ let latestChoice = null;
62
+ /**
63
+ * Safari keeps a cookie page JavaScript wrote for seven days at most, so the
64
+ * server writes the choice again for its full year. Writes go one at a time
65
+ * and only the newest is sent. A response for an older choice still sets that
66
+ * choice's cookie, so the newest is written back over it at once.
67
+ */
68
+ function storeChoiceOnServer(choice) {
69
+ latestChoice = choice;
70
+ if (agentNativeApiDisabledReason() || typeof fetch !== "function")
71
+ return;
72
+ serverChoiceWrites = serverChoiceWrites.then(async () => {
73
+ if (latestChoice !== choice)
74
+ return;
75
+ await fetch(agentNativePath(CONSENT_PATH), {
76
+ method: "POST",
77
+ credentials: "same-origin",
78
+ keepalive: true,
79
+ headers: { "Content-Type": "application/json" },
80
+ body: JSON.stringify({ choice: serializeConsent(choice) }),
81
+ }).catch(() => {
82
+ // coercion-ok: the browser's own copy still holds the choice, for
83
+ // seven days in Safari; the visitor is asked again after that.
84
+ });
85
+ if (latestChoice && latestChoice !== choice) {
86
+ writeChoiceCookie(latestChoice);
87
+ }
88
+ });
89
+ }
90
+ function readCachedRegion() {
91
+ try {
92
+ const value = window.sessionStorage.getItem(REGION_STORAGE_KEY);
93
+ return value === "consent" || value === "none" ? value : null;
94
+ }
95
+ catch {
96
+ // coercion-ok: without session storage the region is asked again.
97
+ return null;
98
+ }
99
+ }
100
+ function cacheRegion(region) {
101
+ try {
102
+ window.sessionStorage.setItem(REGION_STORAGE_KEY, region);
103
+ }
104
+ catch {
105
+ // coercion-ok: without session storage the region is asked again.
106
+ }
107
+ }
108
+ function timeZoneRegion() {
109
+ try {
110
+ const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
111
+ return isConsentRegionTimeZone(timeZone) ? "consent" : "none";
112
+ }
113
+ catch {
114
+ // An unreadable zone could be anywhere, so it gets the banner.
115
+ return "consent";
116
+ }
117
+ }
118
+ async function fetchRegion() {
119
+ if (agentNativeApiDisabledReason() || typeof fetch !== "function") {
120
+ return timeZoneRegion();
121
+ }
122
+ try {
123
+ const response = await fetch(agentNativePath(CONSENT_REGION_PATH), {
124
+ credentials: "same-origin",
125
+ });
126
+ const body = response.ok
127
+ ? (await response.json())
128
+ : {};
129
+ if (body.region === "consent" || body.region === "none") {
130
+ return body.region;
131
+ }
132
+ }
133
+ catch {
134
+ // coercion-ok: with no answer from the server, the time zone decides.
135
+ }
136
+ return timeZoneRegion();
137
+ }
138
+ /**
139
+ * Whether this visitor gets the banner: the server's answer from the
140
+ * request's country, else the browser's time zone. Asked once a session.
141
+ */
142
+ export function resolveConsentRegion() {
143
+ const current = state();
144
+ if (current.region)
145
+ return Promise.resolve(current.region);
146
+ const cached = typeof window === "undefined" ? null : readCachedRegion();
147
+ if (cached) {
148
+ current.region = cached;
149
+ // Another app or tab may have stored a reject since this page's waiters
150
+ // last looked; let them clear what they kept.
151
+ settle();
152
+ notify();
153
+ return Promise.resolve(cached);
154
+ }
155
+ return (current.regionPromise ??= fetchRegion().then((region) => {
156
+ current.region = region;
157
+ cacheRegion(region);
158
+ settle();
159
+ notify();
160
+ return region;
161
+ }));
162
+ }
163
+ /** The best answer available now, without waiting for the server. */
164
+ function regionGuess() {
165
+ const current = state();
166
+ return current.region ?? readCachedRegion() ?? timeZoneRegion();
167
+ }
168
+ function allows(category, region) {
169
+ const choice = getConsentChoice();
170
+ if (choice)
171
+ return choice[category];
172
+ return region === "none";
173
+ }
174
+ /**
175
+ * Whether `category` is allowed right now, on the time zone's guess until the
176
+ * server answers. Pair it with `whenConsentAllows` and a `revoke` that undoes
177
+ * whatever this let through.
178
+ */
179
+ export function consentAllowsNow(category) {
180
+ if (typeof window === "undefined")
181
+ return false;
182
+ if (!state().region)
183
+ void resolveConsentRegion();
184
+ return allows(category, regionGuess());
185
+ }
186
+ function settle() {
187
+ const region = state().region;
188
+ for (const waiter of state().waiters) {
189
+ const allowed = allows(waiter.category, region);
190
+ if (allowed && !waiter.ran) {
191
+ waiter.ran = true;
192
+ isolate(waiter.run);
193
+ }
194
+ else if (!allowed && region) {
195
+ // Also clears what an earlier page stored before the banner existed.
196
+ waiter.ran = false;
197
+ if (waiter.revoke)
198
+ isolate(waiter.revoke);
199
+ }
200
+ }
201
+ }
202
+ /**
203
+ * One callback throwing must not skip the waiters after it, or a later revoke
204
+ * would never clear what a rejected category stored. Rethrow it on its own task
205
+ * so it still surfaces.
206
+ */
207
+ function isolate(callback) {
208
+ try {
209
+ callback();
210
+ }
211
+ catch (error) {
212
+ setTimeout(() => {
213
+ throw error;
214
+ });
215
+ }
216
+ }
217
+ function notify() {
218
+ for (const listener of state().listeners)
219
+ listener();
220
+ }
221
+ /**
222
+ * Run `run` once `category` is allowed: at once outside the consent region,
223
+ * and inside it when the visitor accepts. A later "reject" calls `revoke`.
224
+ */
225
+ export function whenConsentAllows(category, run, options = {}) {
226
+ if (typeof window === "undefined")
227
+ return;
228
+ const waiter = { category, run, revoke: options.revoke, ran: false };
229
+ state().waiters.push(waiter);
230
+ const region = state().region ??
231
+ readCachedRegion() ??
232
+ (options.optimistic ? regionGuess() : null);
233
+ if (allows(category, region)) {
234
+ waiter.ran = true;
235
+ isolate(run);
236
+ }
237
+ if (!state().region)
238
+ void resolveConsentRegion();
239
+ }
240
+ /** Store the visitor's choice for every app on this site, and apply it. */
241
+ export function setConsentChoice(choice) {
242
+ if (typeof document === "undefined")
243
+ return;
244
+ writeChoiceCookie(choice);
245
+ storeChoiceOnServer(choice);
246
+ state().settingsOpen = false;
247
+ if (!state().region)
248
+ state().region = "consent";
249
+ settle();
250
+ notify();
251
+ }
252
+ /** Reopen the banner so the visitor can change their choice. */
253
+ export function openConsentSettings() {
254
+ state().settingsOpen = true;
255
+ notify();
256
+ }
257
+ /** Whether the banner should show now, for the banner component. */
258
+ export function shouldShowConsentBanner() {
259
+ const current = state();
260
+ if (current.settingsOpen)
261
+ return true;
262
+ return current.region === "consent" && !getConsentChoice();
263
+ }
264
+ export function subscribeConsent(listener) {
265
+ state().listeners.add(listener);
266
+ return () => state().listeners.delete(listener);
267
+ }
268
+ /** Run the scripts the page queued before this module loaded. */
269
+ function drainScriptQueue() {
270
+ if (typeof window === "undefined")
271
+ return;
272
+ const holder = window;
273
+ const queued = holder[SCRIPT_QUEUE_KEY];
274
+ const push = (...entries) => {
275
+ for (const [category, run] of entries)
276
+ whenConsentAllows(category, run);
277
+ return 0;
278
+ };
279
+ holder[SCRIPT_QUEUE_KEY] = { push };
280
+ if (Array.isArray(queued))
281
+ push(...queued);
282
+ }
283
+ drainScriptQueue();
@@ -269,6 +269,8 @@ export declare function useResourceSuggestions(params: ListResourceSuggestionsPa
269
269
  export declare function useCreateResourceSuggestion(): import("@tanstack/react-query").UseMutationResult<ResourceSuggestion, Error, CreateResourceSuggestionInput, unknown>;
270
270
  export declare function useCreateResourceSuggestionProposal(): import("@tanstack/react-query").UseMutationResult<ResourceSuggestionProposalResult, Error, CreateResourceSuggestionProposalInput, unknown>;
271
271
  export declare function useDecideResourceSuggestionProposal(): import("@tanstack/react-query").UseMutationResult<ResourceSuggestionProposalResult, Error, DecideResourceSuggestionProposalInput, unknown>;
272
+ /** Shows every listed proposal member as decided until the server answers. */
273
+ export declare function proposalDecisionOverlay(input: DecideResourceSuggestionProposalInput, queryClient: QueryClient): ReviewOptimisticMutation;
272
274
  export declare function useDecideResourceSuggestion(): import("@tanstack/react-query").UseMutationResult<{
273
275
  suggestion: ResourceSuggestion;
274
276
  decision: unknown;
@@ -547,7 +547,34 @@ export function useCreateResourceSuggestionProposal() {
547
547
  return useActionMutation("create-resource-suggestion-proposal");
548
548
  }
549
549
  export function useDecideResourceSuggestionProposal() {
550
- return useActionMutation("decide-resource-suggestion-proposal");
550
+ return useOptimisticReviewMutation("decide-resource-suggestion-proposal", proposalDecisionOverlay);
551
+ }
552
+ /** Shows every listed proposal member as decided until the server answers. */
553
+ export function proposalDecisionOverlay(input, queryClient) {
554
+ const [first] = input.members;
555
+ const resource = first && resourceForSuggestion(queryClient, first.id);
556
+ if (!resource)
557
+ return noSuggestionOverlay();
558
+ const memberIds = new Set(input.members.map((member) => member.id));
559
+ return {
560
+ action: "list-resource-suggestions",
561
+ resource,
562
+ transform: (data, params) => updateSuggestions(data, (suggestions) => suggestions.flatMap((suggestion) => {
563
+ if (!memberIds.has(suggestion.id))
564
+ return [suggestion];
565
+ const next = { ...suggestion, status: input.decision };
566
+ return matchesSuggestionQuery(params, next) ? [next] : [];
567
+ })),
568
+ onSuccess: (result) => (data, params) => {
569
+ const decided = new Map(result.suggestions.map((suggestion) => [suggestion.id, suggestion]));
570
+ return updateSuggestions(data, (suggestions) => suggestions.flatMap((suggestion) => {
571
+ const next = decided.get(suggestion.id);
572
+ if (!next)
573
+ return [suggestion];
574
+ return matchesSuggestionQuery(params, next) ? [next] : [];
575
+ }));
576
+ },
577
+ };
551
578
  }
552
579
  export function useDecideResourceSuggestion() {
553
580
  return useOptimisticReviewMutation("decide-resource-suggestion", (input, queryClient) => {
@@ -30,8 +30,9 @@ export interface ResourceAccessGateOptions {
30
30
  * Called once when the status says the viewer can open the resource: either
31
31
  * the read that failed should be tried again, or the owner shared it while
32
32
  * this screen was open. The status is checked again whenever the window
33
- * regains focus, including switching back from another browser window, and
34
- * every half minute while the viewer's request is open.
33
+ * regains focus, including switching back from another browser window,
34
+ * every half minute while the viewer's request is open, and every 12 seconds
35
+ * while the resource is in Trash.
35
36
  */
36
37
  onAccessGranted?: () => void;
37
38
  }
@@ -4,6 +4,10 @@ import { useActionMutation, useActionQuery } from "../use-action.js";
4
4
  // While a request is open, approving it should open the resource without a
5
5
  // manual reload, even when the window never loses focus.
6
6
  const PENDING_REQUEST_POLL_MS = 30_000;
7
+ // This screen replaces the resource's own view, and with it that view's sync
8
+ // poll, so a restore made elsewhere would otherwise wait for focus or the
9
+ // app-wide idle poll, which backs off to minutes. 12 s is that view's poll.
10
+ const TRASHED_POLL_MS = 12_000;
7
11
  function requestFailure(error) {
8
12
  const failure = error;
9
13
  return {
@@ -24,7 +28,11 @@ export function useResourceAccessGate({ resourceType, resourceId, enabled = true
24
28
  enabled,
25
29
  refetchOnWindowFocus: "always",
26
30
  staleTime: 0,
27
- refetchInterval: (current) => current.state.data?.request ? PENDING_REQUEST_POLL_MS : false,
31
+ refetchInterval: (current) => current.state.data?.state === "trashed"
32
+ ? TRASHED_POLL_MS
33
+ : current.state.data?.request
34
+ ? PENDING_REQUEST_POLL_MS
35
+ : false,
28
36
  });
29
37
  const request = useActionMutation("request-resource-access");
30
38
  const state = query.data?.state;
@@ -0,0 +1,35 @@
1
+ import { type ThemeFamily } from "../appearance/theme-family.js";
2
+ export { DEFAULT_THEME_FAMILY, isThemeFamily, THEME_FAMILY_APP_STATE_KEY, THEME_FAMILY_ATTRIBUTE, THEME_FAMILY_STORAGE_KEY, themeFamilyAppStateKey, themeFamilyStorageKey, type ThemeFamily, } from "../appearance/theme-family.js";
3
+ export declare const THEME_FAMILY_CHANGE_EVENT = "agent-native:theme-family-change";
4
+ export type ThemeFamilyStorageStatus = "valid" | "missing" | "invalid" | "unreadable";
5
+ export type StoredThemeFamily = {
6
+ status: "valid";
7
+ family: ThemeFamily;
8
+ } | {
9
+ status: "missing";
10
+ } | {
11
+ status: "invalid";
12
+ value: string;
13
+ } | {
14
+ status: "unreadable";
15
+ error: Error;
16
+ };
17
+ export interface ThemeFamilyApplyResult {
18
+ family: ThemeFamily;
19
+ storageStatus: "saved" | "unavailable";
20
+ }
21
+ export interface ThemeFamilyPreference {
22
+ family: ThemeFamily;
23
+ source: "default" | "user-setting";
24
+ appId: string | null;
25
+ }
26
+ export declare function readStoredThemeFamily(appId?: string | null): StoredThemeFamily;
27
+ export declare function applyThemeFamily(family: ThemeFamily, appId?: string | null): ThemeFamilyApplyResult;
28
+ export interface ThemeFamilyHook {
29
+ family: ThemeFamily;
30
+ status: "loading" | "success" | "error";
31
+ error?: Error;
32
+ storageStatus: ThemeFamilyStorageStatus;
33
+ setFamily: (family: ThemeFamily) => Promise<ThemeFamily>;
34
+ }
35
+ export declare function useThemeFamily(): ThemeFamilyHook;