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

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 (190) hide show
  1. package/dist/action.d.ts +6 -2
  2. package/dist/agent/engine/builder-engine.js +6 -1
  3. package/dist/agent/engine/tool-call-journal-seed.d.ts +2 -0
  4. package/dist/agent/engine/tool-call-journal-seed.js +14 -0
  5. package/dist/agent/harness/store.js +14 -0
  6. package/dist/agent/production-agent.js +257 -17
  7. package/dist/agent/reaped-run-thread.js +6 -0
  8. package/dist/agent/run-manager.js +6 -0
  9. package/dist/agent/tool-call-journal.d.ts +4 -0
  10. package/dist/agent/tool-call-journal.js +12 -0
  11. package/dist/agent/types.d.ts +7 -1
  12. package/dist/app-config/agent.d.ts +3 -0
  13. package/dist/app-config/agent.js +7 -0
  14. package/dist/app-config/schema.d.ts +3 -0
  15. package/dist/application-state/handlers.d.ts +3 -3
  16. package/dist/application-state/handlers.js +6 -1
  17. package/dist/application-state/index.d.ts +3 -2
  18. package/dist/application-state/index.js +3 -2
  19. package/dist/application-state/last-used-tab.d.ts +11 -0
  20. package/dist/application-state/last-used-tab.js +43 -0
  21. package/dist/application-state/script-helpers.d.ts +26 -0
  22. package/dist/application-state/script-helpers.js +83 -15
  23. package/dist/application-state/store.d.ts +8 -0
  24. package/dist/application-state/store.js +16 -0
  25. package/dist/cli/agent.js +7 -3
  26. package/dist/cli/code-agent-executor.d.ts +1 -0
  27. package/dist/cli/code-agent-executor.js +37 -2
  28. package/dist/cli/code.js +3 -1
  29. package/dist/client/agent-sidebar-state.d.ts +2 -0
  30. package/dist/client/browser-tab-use.d.ts +9 -0
  31. package/dist/client/browser-tab-use.js +112 -0
  32. package/dist/client/chat/runtime.js +3 -1
  33. package/dist/client/error-capture.js +33 -1
  34. package/dist/client/navigation/index.d.ts +1 -0
  35. package/dist/client/navigation/index.js +1 -0
  36. package/dist/client/resources/mcp-integration-logos.js +16 -0
  37. package/dist/client/review/use-review.d.ts +28 -0
  38. package/dist/client/review/use-review.js +41 -12
  39. package/dist/client/route-state.js +2 -0
  40. package/dist/client/sse-event-processor.d.ts +1 -0
  41. package/dist/client/sse-event-processor.js +7 -1
  42. package/dist/client/use-db-sync.d.ts +8 -1
  43. package/dist/client/use-db-sync.js +6 -5
  44. package/dist/coding-tools/run-code.js +12 -2
  45. package/dist/coding-tools/sandbox/executions-store.d.ts +1 -1
  46. package/dist/coding-tools/sandbox/executions-store.js +9 -3
  47. package/dist/collab/routes.d.ts +1 -1
  48. package/dist/data-programs/execute.js +1 -1
  49. package/dist/db/schema-snapshot.generated.js +17 -3
  50. package/dist/extensions/url-safety.d.ts +1 -3
  51. package/dist/extensions/url-safety.js +23 -23
  52. package/dist/file-upload/actions/upload-image.d.ts +5 -5
  53. package/dist/file-upload/index.d.ts +1 -1
  54. package/dist/file-upload/index.js +1 -1
  55. package/dist/file-upload/s3-client.d.ts +1 -0
  56. package/dist/file-upload/s3-client.js +70 -13
  57. package/dist/file-upload/s3.d.ts +9 -0
  58. package/dist/file-upload/s3.js +14 -1
  59. package/dist/framework-package-versions.json +8 -8
  60. package/dist/jobs/run-history.js +10 -3
  61. package/dist/jobs/scheduler.js +5 -36
  62. package/dist/localization/core-messages/supplemental/ar-SA.js +35 -0
  63. package/dist/localization/core-messages/supplemental/de-DE.js +35 -0
  64. package/dist/localization/core-messages/supplemental/en-US.js +35 -0
  65. package/dist/localization/core-messages/supplemental/es-ES.js +35 -0
  66. package/dist/localization/core-messages/supplemental/fr-FR.js +35 -0
  67. package/dist/localization/core-messages/supplemental/hi-IN.js +35 -0
  68. package/dist/localization/core-messages/supplemental/ja-JP.js +35 -0
  69. package/dist/localization/core-messages/supplemental/ko-KR.js +35 -0
  70. package/dist/localization/core-messages/supplemental/pt-BR.js +35 -0
  71. package/dist/localization/core-messages/supplemental/zh-CN.js +35 -0
  72. package/dist/localization/core-messages/supplemental/zh-TW.js +35 -0
  73. package/dist/localization/default-messages.d.ts +35 -0
  74. package/dist/localization/default-messages.js +35 -0
  75. package/dist/localization/mcp-settings-messages.d.ts +35 -0
  76. package/dist/mcp/actions/list-mcp-client-connections.d.ts +8 -0
  77. package/dist/mcp/actions/list-mcp-client-connections.js +39 -0
  78. package/dist/mcp/browser-tab-target.d.ts +16 -0
  79. package/dist/mcp/browser-tab-target.js +58 -0
  80. package/dist/mcp/build-server.d.ts +1 -0
  81. package/dist/mcp/build-server.js +38 -7
  82. package/dist/mcp/call-attribution.d.ts +4 -1
  83. package/dist/mcp/call-attribution.js +3 -1
  84. package/dist/mcp/client-connections.d.ts +6 -0
  85. package/dist/mcp/client-connections.js +91 -0
  86. package/dist/mcp/connect-route.js +12 -2
  87. package/dist/mcp/connect-store.d.ts +3 -0
  88. package/dist/mcp/connect-store.js +10 -1
  89. package/dist/mcp/direct-action-call.js +1 -0
  90. package/dist/mcp/oauth-store.d.ts +6 -0
  91. package/dist/mcp/oauth-store.js +23 -0
  92. package/dist/mcp/oauth-token.d.ts +1 -0
  93. package/dist/mcp/oauth-token.js +1 -0
  94. package/dist/mcp/server.js +9 -20
  95. package/dist/mcp-client/config.d.ts +2 -0
  96. package/dist/mcp-client/manager.js +23 -0
  97. package/dist/mcp-client/workspace-servers.js +42 -10
  98. package/dist/notifications/routes.d.ts +3 -3
  99. package/dist/org/handlers.d.ts +1 -1
  100. package/dist/org/handlers.js +21 -17
  101. package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
  102. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  103. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  104. package/dist/provider-api/index.js +17 -36
  105. package/dist/review/actions/create-review-comment.d.ts +7 -0
  106. package/dist/review/actions/create-review-comment.js +29 -3
  107. package/dist/review/actions/delete-review-comment.js +2 -2
  108. package/dist/review/actions/list-review-comments.d.ts +3 -0
  109. package/dist/review/actions/list-review-comments.js +41 -17
  110. package/dist/review/actions/react-to-review-comment.js +2 -2
  111. package/dist/review/actions/reply-review-comment.d.ts +4 -0
  112. package/dist/review/actions/reply-review-comment.js +15 -6
  113. package/dist/review/actions/update-review-comment-anchor.d.ts +6 -0
  114. package/dist/review/actions/update-review-comment-anchor.js +39 -10
  115. package/dist/review/actions/update-review-comment.d.ts +3 -0
  116. package/dist/review/actions/update-review-comment.js +28 -3
  117. package/dist/review/errors.d.ts +33 -0
  118. package/dist/review/errors.js +51 -0
  119. package/dist/review/index.d.ts +5 -4
  120. package/dist/review/index.js +3 -2
  121. package/dist/review/registry.d.ts +51 -1
  122. package/dist/review/registry.js +117 -0
  123. package/dist/review/store.d.ts +59 -14
  124. package/dist/review/store.js +622 -101
  125. package/dist/review/suggestions/actions.js +65 -10
  126. package/dist/review/suggestions/actor-kind.d.ts +5 -0
  127. package/dist/review/suggestions/actor-kind.js +20 -0
  128. package/dist/review/suggestions/store.js +16 -2
  129. package/dist/review/suggestions/types.d.ts +13 -0
  130. package/dist/review/types.d.ts +89 -0
  131. package/dist/scripts/db/dev-query-proxy.d.ts +2 -0
  132. package/dist/scripts/db/dev-query-proxy.js +4 -1
  133. package/dist/scripts/db/query.js +3 -0
  134. package/dist/scripts/dev/shell.js +4 -0
  135. package/dist/scripts/dev-session.d.ts +31 -0
  136. package/dist/scripts/dev-session.js +58 -0
  137. package/dist/scripts/resources/list.js +53 -4
  138. package/dist/scripts/runner.js +22 -4
  139. package/dist/search/query.d.ts +11 -5
  140. package/dist/search/query.js +52 -15
  141. package/dist/search/tokenize.d.ts +1 -0
  142. package/dist/search/tokenize.js +3 -0
  143. package/dist/secrets/api-keys.d.ts +4 -0
  144. package/dist/secrets/api-keys.js +5 -0
  145. package/dist/secrets/register.d.ts +2 -0
  146. package/dist/secrets/routes.d.ts +2 -2
  147. package/dist/secrets/routes.js +85 -6
  148. package/dist/secrets/storage.d.ts +2 -0
  149. package/dist/secrets/storage.js +34 -9
  150. package/dist/secrets/substitution.d.ts +3 -2
  151. package/dist/secrets/substitution.js +56 -7
  152. package/dist/server/action-discovery.js +5 -0
  153. package/dist/server/action-routes.js +8 -6
  154. package/dist/server/agent-chat/prompt-resources.js +117 -79
  155. package/dist/server/agent-chat/script-entries.js +15 -8
  156. package/dist/server/agent-chat-plugin.js +9 -4
  157. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  158. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  159. package/dist/server/agents-bundle.d.ts +3 -1
  160. package/dist/server/agents-bundle.js +4 -4
  161. package/dist/server/csrf.js +1 -0
  162. package/dist/server/dev-action-bridge.d.ts +2 -0
  163. package/dist/server/dev-action-bridge.js +34 -5
  164. package/dist/server/first-touch-route.d.ts +1 -1
  165. package/dist/server/http-response-telemetry.js +4 -0
  166. package/dist/server/loopback.js +3 -1
  167. package/dist/server/request-context.d.ts +26 -0
  168. package/dist/shared/app-state-routes.d.ts +7 -0
  169. package/dist/shared/app-state-routes.js +7 -0
  170. package/dist/shared/mcp-client-connections.d.ts +41 -0
  171. package/dist/shared/mcp-client-connections.js +18 -0
  172. package/dist/shared/mcp-connect-content.d.ts +18 -0
  173. package/dist/shared/mcp-connect-content.js +200 -172
  174. package/dist/shared/runtime-config.js +5 -1
  175. package/dist/templates/chat/.agents/skills/context-awareness/SKILL.md +13 -0
  176. package/dist/templates/default/.agents/skills/context-awareness/SKILL.md +13 -0
  177. package/dist/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +13 -0
  178. package/dist/tracking/providers.d.ts +7 -0
  179. package/dist/tracking/providers.js +39 -17
  180. package/dist/triggers/condition-evaluator.d.ts +2 -0
  181. package/dist/triggers/condition-evaluator.js +93 -3
  182. package/dist/triggers/dispatcher.js +24 -6
  183. package/docs/AGENTS.md +16 -16
  184. package/docs/content/create-with-an-agent.mdx +10 -4
  185. package/docs/content/durable-resume.mdx +11 -2
  186. package/docs/content/toolkit-comments-review.mdx +54 -1
  187. package/package.json +5 -4
  188. package/src/templates/chat/.agents/skills/context-awareness/SKILL.md +13 -0
  189. package/src/templates/default/.agents/skills/context-awareness/SKILL.md +13 -0
  190. package/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +13 -0
@@ -4,7 +4,7 @@ import { sanitizeReviewCommentMetadata } from "../attachments.js";
4
4
  import { reviewAuthorNameFromContext } from "../identity.js";
5
5
  import { extractReviewMentions, normalizeReviewMentions } from "../mentions.js";
6
6
  import { notifyReviewComment, notifyReviewCommentWithReceipt, } from "../notifications.js";
7
- import { assertReviewableResourceAccess, normalizeReviewVisibility, } from "../registry.js";
7
+ import { assertPersonReviewAnchorKind, assertReviewCommentAccess, normalizeReviewVisibility, reviewCommentPolicy, } from "../registry.js";
8
8
  import { insertReviewComment, insertReviewCommentIdempotently, reviewCommentIdForClientOperation, } from "../store.js";
9
9
  const mentionSchema = z.object({
10
10
  label: z.string().min(1),
@@ -15,6 +15,12 @@ const schema = z.object({
15
15
  resourceType: z.string().min(1),
16
16
  resourceId: z.string().min(1),
17
17
  targetId: z.string().nullable().optional(),
18
+ surfaceId: z
19
+ .string()
20
+ .min(1)
21
+ .nullable()
22
+ .optional()
23
+ .describe("The part of the resource the thread sits on, such as a slide"),
18
24
  kind: z
19
25
  .enum([
20
26
  "comment",
@@ -26,6 +32,19 @@ const schema = z.object({
26
32
  ])
27
33
  .optional(),
28
34
  anchor: z.unknown().nullable().optional(),
35
+ anchorKind: z
36
+ .string()
37
+ .min(1)
38
+ .nullable()
39
+ .optional()
40
+ .describe("The anchor's kind, when the resource declares anchor kinds"),
41
+ anchorVersion: z
42
+ .number()
43
+ .int()
44
+ .positive()
45
+ .nullable()
46
+ .optional()
47
+ .describe("The version the anchor was written at; defaults to the latest"),
29
48
  body: z.string().min(1),
30
49
  authorName: z.string().nullable().optional(),
31
50
  resolutionTarget: z.enum(["agent", "human"]).nullable().optional(),
@@ -47,18 +66,25 @@ export default defineAction({
47
66
  },
48
67
  run: async (args, ctx) => {
49
68
  const actionCtx = ctx;
50
- const access = await assertReviewableResourceAccess(args.resourceType, args.resourceId, actionCtx, "commenter");
69
+ const access = await assertReviewCommentAccess(args.resourceType, args.resourceId, actionCtx);
51
70
  const mentions = normalizeReviewMentions([
52
71
  ...normalizeReviewMentions(args.mentions),
53
72
  ...extractReviewMentions(args.body),
54
73
  ]);
55
- const resolutionTarget = args.resolutionTarget ?? (mentions.length > 0 ? "human" : "agent");
74
+ const resolutionTarget = args.resolutionTarget ??
75
+ (mentions.length > 0
76
+ ? "human"
77
+ : reviewCommentPolicy(args.resourceType).unmentionedRoute);
78
+ assertPersonReviewAnchorKind(args.anchorKind);
56
79
  const input = {
57
80
  resourceType: args.resourceType,
58
81
  resourceId: args.resourceId,
59
82
  targetId: args.targetId ?? null,
83
+ surfaceId: args.surfaceId ?? null,
60
84
  kind: (args.kind ?? "comment"),
61
85
  anchor: args.anchor ?? null,
86
+ anchorKind: args.anchorKind ?? null,
87
+ anchorVersion: args.anchorVersion ?? null,
62
88
  body: args.body,
63
89
  authorEmail: actionCtx?.userEmail ?? null,
64
90
  authorName: args.authorName ?? reviewAuthorNameFromContext(actionCtx),
@@ -2,7 +2,7 @@ import { z } from "zod";
2
2
  import { defineAction, fail } from "../../action.js";
3
3
  import { ForbiddenError } from "../../sharing/access.js";
4
4
  import { roleSatisfies } from "../../sharing/schema.js";
5
- import { resolveReviewableResourceAccess } from "../registry.js";
5
+ import { canCommentOnReviewableResource, resolveReviewableResourceAccess, } from "../registry.js";
6
6
  import { deleteReviewComment, getReviewCommentById } from "../store.js";
7
7
  import { canDeleteReviewThread } from "../thread-delete.js";
8
8
  const schema = z.object({
@@ -39,7 +39,7 @@ export default defineAction({
39
39
  const isAuthor = Boolean(actionCtx?.userEmail) &&
40
40
  normalizeEmail(comment.authorEmail) ===
41
41
  normalizeEmail(actionCtx?.userEmail);
42
- if (!access || !roleSatisfies(access.role, "commenter")) {
42
+ if (!access || !canCommentOnReviewableResource(args.resourceType, access)) {
43
43
  throw new ForbiddenError("Not allowed to delete this review comment");
44
44
  }
45
45
  const isEditor = roleSatisfies(access.role, "editor");
@@ -4,10 +4,13 @@ declare const _default: import("../../action.js").ActionDefinition<{
4
4
  includeResolved?: boolean | undefined;
5
5
  includeDeleted?: boolean | undefined;
6
6
  targetId?: string | null | undefined;
7
+ surfaceId?: string | null | undefined;
7
8
  newestFirst?: boolean | undefined;
8
9
  limit?: number | undefined;
10
+ cursor?: string | undefined;
9
11
  }, {
10
12
  comments: import("../types.js").ReviewComment[];
13
+ nextCursor: string | null | undefined;
11
14
  reviewStatus: import("../types.js").ReviewStatusEntry | null;
12
15
  summary: import("../store.js").ReviewThreadSummary;
13
16
  discussion: {
@@ -5,8 +5,8 @@ import { isEmailDerivedName, resolveUserProfileName, } from "../../user-profile/
5
5
  import { getUserProfiles } from "../../user-profile/store.js";
6
6
  import { sanitizeReviewCommentMetadata } from "../attachments.js";
7
7
  import { redactPublicReviewCommentIdentity, redactPublicReviewStatusIdentity, shouldRedactReviewIdentity, } from "../identity.js";
8
- import { assertReviewableResourceAccess } from "../registry.js";
9
- import { getLiveReviewAuthorsByThread, getReviewStatus, getReviewDiscussionStateForComments, getReviewThreadSummary, queryReviewComments, } from "../store.js";
8
+ import { assertReviewableResourceAccess, canCommentOnReviewableResource, } from "../registry.js";
9
+ import { getLiveReviewAuthorsByThread, getReviewStatus, getReviewDiscussionStateForComments, getReviewThreadSummary, queryReviewCommentPage, queryReviewComments, } from "../store.js";
10
10
  import { canDeleteReviewThread } from "../thread-delete.js";
11
11
  const schema = z.object({
12
12
  resourceType: z.string().min(1),
@@ -14,8 +14,19 @@ const schema = z.object({
14
14
  includeResolved: z.boolean().optional(),
15
15
  includeDeleted: z.boolean().optional(),
16
16
  targetId: z.string().nullable().optional(),
17
+ surfaceId: z
18
+ .string()
19
+ .min(1)
20
+ .nullable()
21
+ .optional()
22
+ .describe("One surface's comments, or null for those on no surface"),
17
23
  newestFirst: z.boolean().optional(),
18
24
  limit: z.number().int().positive().max(500).optional(),
25
+ cursor: z
26
+ .string()
27
+ .min(1)
28
+ .optional()
29
+ .describe("The nextCursor from the previous page; pages run oldest first only"),
19
30
  });
20
31
  export default defineAction({
21
32
  description: "List inline comments, annotations, and review threads for a resource.",
@@ -31,18 +42,28 @@ export default defineAction({
31
42
  userEmail: actionCtx?.userEmail ?? null,
32
43
  orgId: actionCtx?.orgId ?? null,
33
44
  };
34
- const [rawComments, reviewStatus, summary] = await Promise.all([
35
- queryReviewComments({
36
- resourceType: args.resourceType,
37
- resourceId: args.resourceId,
38
- scope,
39
- bypassScope: true,
40
- includeResolved: args.includeResolved,
41
- includeDeleted: args.includeDeleted,
42
- targetId: args.targetId,
43
- newestFirst: args.newestFirst,
44
- limit: args.limit,
45
- }),
45
+ const query = {
46
+ resourceType: args.resourceType,
47
+ resourceId: args.resourceId,
48
+ scope,
49
+ bypassScope: true,
50
+ includeResolved: args.includeResolved,
51
+ includeDeleted: args.includeDeleted,
52
+ targetId: args.targetId,
53
+ surfaceId: args.surfaceId,
54
+ newestFirst: args.newestFirst,
55
+ limit: args.limit,
56
+ };
57
+ // Newest first lists whole threads by latest activity, which no cursor
58
+ // can resume, so only oldest first pages; a cursor with it is refused.
59
+ const [page, reviewStatus, summary] = await Promise.all([
60
+ args.newestFirst && !args.cursor
61
+ ? queryReviewComments(query).then((comments) => ({
62
+ comments,
63
+ // Not paged, so no cursor; null would claim the list is complete.
64
+ nextCursor: undefined,
65
+ }))
66
+ : queryReviewCommentPage({ ...query, cursor: args.cursor }),
46
67
  getReviewStatus(args.resourceType, args.resourceId, scope, {
47
68
  bypassScope: true,
48
69
  }),
@@ -52,16 +73,17 @@ export default defineAction({
52
73
  scope,
53
74
  bypassScope: true,
54
75
  targetId: args.targetId,
76
+ surfaceId: args.surfaceId,
55
77
  }),
56
78
  ]);
57
- const comments = await Promise.all(rawComments.map(async (comment) => ({
79
+ const comments = await Promise.all(page.comments.map(async (comment) => ({
58
80
  ...comment,
59
81
  metadata: await sanitizeReviewCommentMetadata(comment.metadata),
60
82
  })));
61
83
  const discussion = {
62
84
  ...(await getReviewDiscussionStateForComments(comments, actionCtx?.userEmail ?? null)),
63
85
  canReact: Boolean(actionCtx?.userEmail) &&
64
- roleSatisfies(access.role, "commenter"),
86
+ canCommentOnReviewableResource(args.resourceType, access),
65
87
  canSetThreadPreferences: Boolean(actionCtx?.userEmail),
66
88
  };
67
89
  const profiles = await getUserProfiles(comments.flatMap((comment) => comment.authorEmail &&
@@ -70,7 +92,7 @@ export default defineAction({
70
92
  : []));
71
93
  const redactIdentity = shouldRedactReviewIdentity(actionCtx, access);
72
94
  const isEditor = roleSatisfies(access.role, "editor");
73
- const canComment = roleSatisfies(access.role, "commenter");
95
+ const canComment = canCommentOnReviewableResource(args.resourceType, access, Boolean(actionCtx?.userEmail));
74
96
  // Only a commenter's own threads can turn on who else wrote in them: an
75
97
  // editor may delete any, and nobody else may delete one they did not start.
76
98
  // So each first comment asked about here is the reader's own, and its
@@ -111,12 +133,14 @@ export default defineAction({
111
133
  return redactIdentity
112
134
  ? {
113
135
  comments: commentsWithCapabilities.map(redactPublicReviewCommentIdentity),
136
+ nextCursor: page.nextCursor,
114
137
  reviewStatus: redactPublicReviewStatusIdentity(reviewStatus),
115
138
  summary,
116
139
  discussion,
117
140
  }
118
141
  : {
119
142
  comments: commentsWithCapabilities,
143
+ nextCursor: page.nextCursor,
120
144
  reviewStatus,
121
145
  summary,
122
146
  discussion,
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { defineAction, fail } from "../../action.js";
3
- import { assertReviewableResourceAccess } from "../registry.js";
3
+ import { assertReviewCommentAccess } from "../registry.js";
4
4
  import { getReviewCommentById, setReviewCommentReaction } from "../store.js";
5
5
  export default defineAction({
6
6
  description: "Add or remove your reaction on a review comment.",
@@ -17,7 +17,7 @@ export default defineAction({
17
17
  openWorldHint: false,
18
18
  },
19
19
  run: async (args, ctx) => {
20
- await assertReviewableResourceAccess(args.resourceType, args.resourceId, ctx, "commenter");
20
+ await assertReviewCommentAccess(args.resourceType, args.resourceId, ctx);
21
21
  const comment = await getReviewCommentById(args.commentId, {
22
22
  userEmail: ctx?.userEmail,
23
23
  orgId: ctx?.orgId,
@@ -20,9 +20,13 @@ declare const _default: import("../../action.js").ActionDefinition<{
20
20
  threadId: string;
21
21
  parentCommentId: string | null;
22
22
  targetId: string | null;
23
+ surfaceId?: string | null;
23
24
  kind: import("../types.js").ReviewCommentKind;
24
25
  status: import("../types.js").ReviewCommentStatus;
25
26
  anchor: unknown;
27
+ anchorKind?: string | null;
28
+ anchorVersion?: number | null;
29
+ revision?: number;
26
30
  body: string;
27
31
  authorEmail: string | null;
28
32
  authorName: string | null;
@@ -4,7 +4,7 @@ import { sanitizeReviewCommentMetadata } from "../attachments.js";
4
4
  import { reviewAuthorNameFromContext } from "../identity.js";
5
5
  import { extractReviewMentions, normalizeReviewMentions } from "../mentions.js";
6
6
  import { notifyReviewComment, notifyReviewCommentWithReceipt, } from "../notifications.js";
7
- import { assertReviewableResourceAccess, normalizeReviewVisibility, } from "../registry.js";
7
+ import { assertReviewCommentAccess, normalizeReviewVisibility, } from "../registry.js";
8
8
  import { getReviewCommentById, getReviewThreadRoot, insertReviewReply, insertReviewReplyIdempotently, ReviewCommentDeletedError, ReviewThreadNotFoundError, ReviewThreadNotOpenError, reviewCommentIdForClientOperation, } from "../store.js";
9
9
  const mentionSchema = z.object({
10
10
  label: z.string().min(1),
@@ -40,7 +40,7 @@ export default defineAction({
40
40
  userEmail: actionCtx?.userEmail ?? null,
41
41
  orgId: actionCtx?.orgId ?? null,
42
42
  };
43
- const access = await assertReviewableResourceAccess(args.resourceType, args.resourceId, actionCtx, "commenter");
43
+ const access = await assertReviewCommentAccess(args.resourceType, args.resourceId, actionCtx);
44
44
  const clientOperationCommentId = args.clientOperationId
45
45
  ? reviewCommentIdForClientOperation(args.clientOperationId)
46
46
  : null;
@@ -87,13 +87,22 @@ export default defineAction({
87
87
  threadId: parent.threadId,
88
88
  parentCommentId: parent.id,
89
89
  targetId: parent.targetId,
90
+ surfaceId: parent.surfaceId,
90
91
  kind: parent.kind,
91
92
  // A retry rebuilds the reply from its parent, whose anchor may have moved
92
93
  // since, or been cleared when the parent was deleted. The saved reply
93
- // keeps the anchor it was sent with.
94
- anchor: existingReply?.parentCommentId === parent.id
95
- ? existingReply.anchor
96
- : parent.anchor,
94
+ // keeps the anchor it was sent with, and its kind and version with it.
95
+ ...(existingReply?.parentCommentId === parent.id
96
+ ? {
97
+ anchor: existingReply.anchor,
98
+ anchorKind: existingReply.anchorKind,
99
+ anchorVersion: existingReply.anchorVersion,
100
+ }
101
+ : {
102
+ anchor: parent.anchor,
103
+ anchorKind: parent.anchorKind,
104
+ anchorVersion: parent.anchorVersion,
105
+ }),
97
106
  body: args.body,
98
107
  authorEmail: actionCtx?.userEmail ?? null,
99
108
  authorName: args.authorName ?? reviewAuthorNameFromContext(actionCtx),
@@ -3,9 +3,15 @@ declare const _default: import("../../action.js").ActionDefinition<{
3
3
  resourceId: string;
4
4
  commentId: string;
5
5
  anchor: unknown;
6
+ anchorKind?: string | null | undefined;
7
+ anchorVersion?: number | null | undefined;
8
+ expectedRevision?: number | undefined;
6
9
  }, {
7
10
  commentId: string;
8
11
  anchor: unknown;
12
+ anchorKind: string | null | undefined;
13
+ anchorVersion: number | null | undefined;
14
+ revision: number | undefined;
9
15
  updatedCount: number;
10
16
  }>;
11
17
  export default _default;
@@ -2,13 +2,26 @@ import { z } from "zod";
2
2
  import { defineAction, fail } from "../../action.js";
3
3
  import { ForbiddenError } from "../../sharing/access.js";
4
4
  import { roleSatisfies } from "../../sharing/schema.js";
5
- import { resolveReviewableResourceAccess } from "../registry.js";
6
- import { getReviewCommentById, updateReviewCommentAnchor } from "../store.js";
5
+ import { assertPersonReviewAnchorKind, canCommentOnReviewableResource, resolveReviewableResourceAccess, } from "../registry.js";
6
+ import { getReviewCommentById, updateReviewComment } from "../store.js";
7
7
  const schema = z.object({
8
8
  resourceType: z.string().min(1),
9
9
  resourceId: z.string().min(1),
10
10
  commentId: z.string().min(1),
11
11
  anchor: z.unknown().nullable(),
12
+ anchorKind: z
13
+ .string()
14
+ .min(1)
15
+ .nullable()
16
+ .optional()
17
+ .describe("A new kind for the anchor; left out, the comment's kind stays"),
18
+ anchorVersion: z.number().int().positive().nullable().optional(),
19
+ expectedRevision: z
20
+ .number()
21
+ .int()
22
+ .positive()
23
+ .optional()
24
+ .describe("The comment's revision when read; a newer one refuses the move with revision_conflict"),
12
25
  });
13
26
  export default defineAction({
14
27
  description: "Move an anchored review comment to a new canvas position.",
@@ -42,25 +55,41 @@ export default defineAction({
42
55
  });
43
56
  }
44
57
  const isAuthor = comment.authorEmail?.trim().toLowerCase() === userEmail;
45
- if (!access || !roleSatisfies(access.role, "commenter")) {
58
+ if (!access || !canCommentOnReviewableResource(args.resourceType, access)) {
46
59
  throw new ForbiddenError("Not allowed to move this review comment");
47
60
  }
48
61
  if (!isAuthor && !roleSatisfies(access.role, "editor")) {
49
62
  throw new ForbiddenError("Not allowed to move this review comment");
50
63
  }
51
- const updatedCount = await updateReviewCommentAnchor({
52
- commentId: comment.id,
53
- resourceType: args.resourceType,
54
- resourceId: args.resourceId,
64
+ // The kind the moved anchor ends up with, worked out as the store does.
65
+ assertPersonReviewAnchorKind(args.anchorKind !== undefined
66
+ ? args.anchorKind
67
+ : args.anchor === null
68
+ ? null
69
+ : comment.anchorKind);
70
+ const moved = await updateReviewComment(comment.id, {
55
71
  anchor: args.anchor,
56
- });
57
- if (updatedCount < 1) {
72
+ ...(args.anchorKind !== undefined
73
+ ? { anchorKind: args.anchorKind }
74
+ : {}),
75
+ ...(args.anchorVersion !== undefined
76
+ ? { anchorVersion: args.anchorVersion }
77
+ : {}),
78
+ }, { resourceType: args.resourceType, resourceId: args.resourceId }, { expectedRevision: args.expectedRevision, actorEmail: userEmail });
79
+ if (!moved) {
58
80
  fail("Review comment could not be moved", {
59
81
  statusCode: 404,
60
82
  errorCode: "not_found",
61
83
  });
62
84
  }
63
- return { commentId: comment.id, anchor: args.anchor, updatedCount };
85
+ return {
86
+ commentId: moved.id,
87
+ anchor: moved.anchor,
88
+ anchorKind: moved.anchorKind,
89
+ anchorVersion: moved.anchorVersion,
90
+ revision: moved.revision,
91
+ updatedCount: 1,
92
+ };
64
93
  },
65
94
  audit: {
66
95
  target: (args) => ({
@@ -4,10 +4,13 @@ declare const _default: import("../../action.js").ActionDefinition<{
4
4
  commentId: string;
5
5
  body?: string | undefined;
6
6
  anchor?: unknown;
7
+ anchorKind?: string | null | undefined;
8
+ anchorVersion?: number | null | undefined;
7
9
  mentions?: {
8
10
  label: string;
9
11
  email?: string | null | undefined;
10
12
  id?: string | null | undefined;
11
13
  }[] | undefined;
14
+ expectedRevision?: number | undefined;
12
15
  }, import("../types.js").ReviewComment>;
13
16
  export default _default;
@@ -3,7 +3,7 @@ import { defineAction } from "../../action.js";
3
3
  import { ForbiddenError } from "../../sharing/access.js";
4
4
  import { roleSatisfies } from "../../sharing/schema.js";
5
5
  import { extractReviewMentions, normalizeReviewMentions } from "../mentions.js";
6
- import { resolveReviewableResourceAccess } from "../registry.js";
6
+ import { assertPersonReviewAnchorKind, canCommentOnReviewableResource, resolveReviewableResourceAccess, } from "../registry.js";
7
7
  import { getReviewCommentById, updateReviewComment } from "../store.js";
8
8
  const mentionSchema = z.object({
9
9
  label: z.string().min(1),
@@ -17,7 +17,15 @@ const schema = z
17
17
  commentId: z.string().min(1),
18
18
  body: z.string().min(1).optional(),
19
19
  anchor: z.unknown().optional(),
20
+ anchorKind: z.string().min(1).nullable().optional(),
21
+ anchorVersion: z.number().int().positive().nullable().optional(),
20
22
  mentions: z.array(mentionSchema).optional(),
23
+ expectedRevision: z
24
+ .number()
25
+ .int()
26
+ .positive()
27
+ .optional()
28
+ .describe("The comment's revision when read; a newer one refuses the edit with revision_conflict"),
21
29
  })
22
30
  .refine((args) => args.body !== undefined || args.anchor !== undefined, {
23
31
  message: "Provide a body or anchor to update",
@@ -50,12 +58,20 @@ export default defineAction({
50
58
  normalizeEmail(actionCtx?.userEmail);
51
59
  const changesContent = args.body !== undefined || args.mentions !== undefined;
52
60
  if (!access ||
53
- !roleSatisfies(access.role, "commenter") ||
61
+ !canCommentOnReviewableResource(args.resourceType, access) ||
54
62
  (changesContent
55
63
  ? !isAuthor
56
64
  : !isAuthor && !roleSatisfies(access.role, "editor"))) {
57
65
  throw new ForbiddenError("Not allowed to update this review comment");
58
66
  }
67
+ if (args.anchor !== undefined) {
68
+ // The kind the moved anchor ends up with, worked out as the store does.
69
+ assertPersonReviewAnchorKind(args.anchorKind !== undefined
70
+ ? args.anchorKind
71
+ : args.anchor === null
72
+ ? null
73
+ : comment.anchorKind);
74
+ }
59
75
  const mentions = args.body === undefined
60
76
  ? undefined
61
77
  : normalizeReviewMentions([
@@ -65,8 +81,17 @@ export default defineAction({
65
81
  const updated = await updateReviewComment(args.commentId, {
66
82
  ...(args.body !== undefined ? { body: args.body } : {}),
67
83
  ...(args.anchor !== undefined ? { anchor: args.anchor } : {}),
84
+ ...(args.anchor !== undefined && args.anchorKind !== undefined
85
+ ? { anchorKind: args.anchorKind }
86
+ : {}),
87
+ ...(args.anchor !== undefined && args.anchorVersion !== undefined
88
+ ? { anchorVersion: args.anchorVersion }
89
+ : {}),
68
90
  ...(mentions !== undefined ? { mentions } : {}),
69
- }, { resourceType: args.resourceType, resourceId: args.resourceId });
91
+ }, { resourceType: args.resourceType, resourceId: args.resourceId }, {
92
+ expectedRevision: args.expectedRevision,
93
+ actorEmail: actionCtx?.userEmail ?? null,
94
+ });
70
95
  if (!updated)
71
96
  throw new Error("Review comment not found");
72
97
  return updated;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Refusals from the kit's storage. Each carries the shape an action reports as
3
+ * its failure (`isActionContractError`), so a store error reaches the caller
4
+ * with its status and code without the store importing the action runtime.
5
+ */
6
+ declare class ReviewStorageError extends Error {
7
+ readonly actionContractError = true;
8
+ readonly errorCode: string;
9
+ readonly statusCode: number;
10
+ readonly details?: Record<string, unknown>;
11
+ constructor(message: string, errorCode: string, statusCode: number, details?: Record<string, unknown>);
12
+ }
13
+ /** An anchor its resource's anchor kind refused, or one with no kind to check it. */
14
+ export declare class ReviewAnchorError extends ReviewStorageError {
15
+ constructor(message: string);
16
+ }
17
+ /** The comment changed after the caller read it; nothing was written. */
18
+ export declare class ReviewCommentRevisionConflictError extends ReviewStorageError {
19
+ readonly currentRevision: number;
20
+ constructor(commentId: string, currentRevision: number);
21
+ }
22
+ /** An outside ID already linked to another comment. */
23
+ export declare class ReviewCommentLinkConflictError extends ReviewStorageError {
24
+ constructor(provider: string, externalKind: string, externalId: string);
25
+ }
26
+ /**
27
+ * A list cursor this kit didn't hand out, one from another list, or one used
28
+ * with another order.
29
+ */
30
+ export declare class ReviewCursorError extends ReviewStorageError {
31
+ constructor(message: string);
32
+ }
33
+ export {};
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Refusals from the kit's storage. Each carries the shape an action reports as
3
+ * its failure (`isActionContractError`), so a store error reaches the caller
4
+ * with its status and code without the store importing the action runtime.
5
+ */
6
+ class ReviewStorageError extends Error {
7
+ actionContractError = true;
8
+ errorCode;
9
+ statusCode;
10
+ details;
11
+ constructor(message, errorCode, statusCode, details) {
12
+ super(message);
13
+ this.errorCode = errorCode;
14
+ this.statusCode = statusCode;
15
+ if (details)
16
+ this.details = details;
17
+ }
18
+ }
19
+ /** An anchor its resource's anchor kind refused, or one with no kind to check it. */
20
+ export class ReviewAnchorError extends ReviewStorageError {
21
+ constructor(message) {
22
+ super(message, "invalid_anchor", 400);
23
+ this.name = "ReviewAnchorError";
24
+ }
25
+ }
26
+ /** The comment changed after the caller read it; nothing was written. */
27
+ export class ReviewCommentRevisionConflictError extends ReviewStorageError {
28
+ currentRevision;
29
+ constructor(commentId, currentRevision) {
30
+ super(`Comment ${commentId} changed since it was read; read it again and retry.`, "revision_conflict", 409, { commentId, currentRevision });
31
+ this.name = "ReviewCommentRevisionConflictError";
32
+ this.currentRevision = currentRevision;
33
+ }
34
+ }
35
+ /** An outside ID already linked to another comment. */
36
+ export class ReviewCommentLinkConflictError extends ReviewStorageError {
37
+ constructor(provider, externalKind, externalId) {
38
+ super(`The ${provider} ${externalKind} ${externalId} is already linked to another comment.`, "link_conflict", 409, { provider, externalKind, externalId });
39
+ this.name = "ReviewCommentLinkConflictError";
40
+ }
41
+ }
42
+ /**
43
+ * A list cursor this kit didn't hand out, one from another list, or one used
44
+ * with another order.
45
+ */
46
+ export class ReviewCursorError extends ReviewStorageError {
47
+ constructor(message) {
48
+ super(message, "invalid_cursor", 400);
49
+ this.name = "ReviewCursorError";
50
+ }
51
+ }
@@ -1,11 +1,12 @@
1
- export type { ReviewActorKind, ReviewComment, ReviewCommentKind, ReviewCommentStatus, ReviewCommentReaction, ReviewThreadPreference, ReviewDiscussionState, ReviewMention, ReviewResolutionTarget, ReviewResourceAccess, ReviewResourceContext, ReviewResourceRole, ReviewScope, ReviewStatus, ReviewStatusEntry, ReviewableResourceRegistration, } from "./types.js";
1
+ export type { ReviewActorKind, ReviewAnchorKind, ReviewComment, ReviewCommentKind, ReviewCommentLink, ReviewCommentPolicy, ReviewCommentStatus, ReviewCommentWriteEvent, ReviewCommentReaction, ReviewThreadPreference, ReviewDiscussionState, ReviewMention, ReviewResolutionTarget, ReviewResourceAccess, ReviewResourceContext, ReviewResourceRole, ReviewScope, ReviewStatus, ReviewStatusEntry, ReviewableResourceRegistration, } from "./types.js";
2
+ export { ReviewAnchorError, ReviewCommentLinkConflictError, ReviewCommentRevisionConflictError, ReviewCursorError, } from "./errors.js";
2
3
  export { redactPublicReviewCommentIdentity, redactPublicReviewStatusIdentity, reviewAuthorNameFromContext, shouldRedactReviewIdentity, } from "./identity.js";
3
4
  export { extractReviewMentions, normalizeReviewMentions } from "./mentions.js";
4
5
  export { notifyReviewComment, REVIEW_NOTIFICATION_PREFS_KEY, type ReviewNotificationResult, } from "./notifications.js";
5
- export { __resetReviewableResourcesForTests, assertReviewableResourceAccess, getReviewableResource, listReviewableResources, registerReviewableResource, resolveReviewableResourceAccess, } from "./registry.js";
6
- export { __resetReviewInitForTests, ensureReviewTables, getReviewCommentById, getReviewStatus, getReviewThreadSummary, getReviewThreadRoot, queryReviewComments, routeReviewThread, sendReviewThreadToAgent, } from "./store.js";
6
+ export { __resetReviewableResourcesForTests, assertReviewableResourceAccess, assertReviewCommentAccess, canCommentOnReviewableResource, getReviewableResource, listReviewableResources, normalizeReviewAnchor, REVIEW_SUGGESTION_ANCHOR_KIND, registerReviewableResource, resolveReviewableResourceAccess, reviewCommentPolicy, } from "./registry.js";
7
+ export { __resetReviewInitForTests, ensureReviewTables, findReviewCommentLink, getReviewCommentById, getReviewStatus, getReviewThreadSummary, getReviewThreadRoot, linkReviewComment, listReviewCommentLinks, queryReviewCommentPage, queryReviewComments, routeReviewThread, sendReviewThreadToAgent, } from "./store.js";
7
8
  export * from "./suggestions/types.js";
8
9
  export { registerSuggestionAdapter, getSuggestionAdapter, listSuggestionAdapters, __resetSuggestionAdaptersForTests, } from "./suggestions/registry.js";
9
10
  export { ensureSuggestionTables, getDecision, getSuggestion, getSuggestionByCreationKey, listSuggestions, rebaseSuggestion, __resetSuggestionTablesForTests, } from "./suggestions/store.js";
10
11
  export { suggestionActorKind, suggestionActorKindMatchesReceipt, } from "./suggestions/actor-kind.js";
11
- export type { GetReviewThreadSummaryInput, ReviewThreadSummary, } from "./store.js";
12
+ export type { GetReviewThreadSummaryInput, QueryReviewCommentsInput, ReviewCommentPage, ReviewCommentPosition, ReviewThreadSummary, } from "./store.js";
@@ -1,8 +1,9 @@
1
+ export { ReviewAnchorError, ReviewCommentLinkConflictError, ReviewCommentRevisionConflictError, ReviewCursorError, } from "./errors.js";
1
2
  export { redactPublicReviewCommentIdentity, redactPublicReviewStatusIdentity, reviewAuthorNameFromContext, shouldRedactReviewIdentity, } from "./identity.js";
2
3
  export { extractReviewMentions, normalizeReviewMentions } from "./mentions.js";
3
4
  export { notifyReviewComment, REVIEW_NOTIFICATION_PREFS_KEY, } from "./notifications.js";
4
- export { __resetReviewableResourcesForTests, assertReviewableResourceAccess, getReviewableResource, listReviewableResources, registerReviewableResource, resolveReviewableResourceAccess, } from "./registry.js";
5
- export { __resetReviewInitForTests, ensureReviewTables, getReviewCommentById, getReviewStatus, getReviewThreadSummary, getReviewThreadRoot, queryReviewComments, routeReviewThread, sendReviewThreadToAgent, } from "./store.js";
5
+ export { __resetReviewableResourcesForTests, assertReviewableResourceAccess, assertReviewCommentAccess, canCommentOnReviewableResource, getReviewableResource, listReviewableResources, normalizeReviewAnchor, REVIEW_SUGGESTION_ANCHOR_KIND, registerReviewableResource, resolveReviewableResourceAccess, reviewCommentPolicy, } from "./registry.js";
6
+ export { __resetReviewInitForTests, ensureReviewTables, findReviewCommentLink, getReviewCommentById, getReviewStatus, getReviewThreadSummary, getReviewThreadRoot, linkReviewComment, listReviewCommentLinks, queryReviewCommentPage, queryReviewComments, routeReviewThread, sendReviewThreadToAgent, } from "./store.js";
6
7
  export * from "./suggestions/types.js";
7
8
  export { registerSuggestionAdapter, getSuggestionAdapter, listSuggestionAdapters, __resetSuggestionAdaptersForTests, } from "./suggestions/registry.js";
8
9
  export { ensureSuggestionTables, getDecision, getSuggestion, getSuggestionByCreationKey, listSuggestions, rebaseSuggestion, __resetSuggestionTablesForTests, } from "./suggestions/store.js";