@agent-native/core-corpus 0.212.1 → 0.214.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.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/onboarding-journeys/SKILL.md +29 -14
- package/corpus/templates/analytics/CHANGELOG.md +11 -0
- package/corpus/templates/analytics/actions/estimate-onboarding-journey-cost.ts +23 -6
- package/corpus/templates/analytics/actions/get-onboarding-journey.ts +20 -5
- package/corpus/templates/analytics/actions/provider-api-register.ts +2 -7
- package/corpus/templates/analytics/changelog/2026-10-21-analytics-recognizes-questions-about-connected-data-product-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-21-onboarding-journeys-now-honor-larger-event-row-budgets.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-21-preserve-unknown-replay-availability-in-partial-journeys.md +6 -0
- package/corpus/templates/analytics/package.json +6 -0
- package/corpus/templates/analytics/scripts/journey-capture-plan.ts +18 -7
- package/corpus/templates/analytics/scripts/journey-capture.ts +3 -1
- package/corpus/templates/analytics/server/lib/db-admin-connections.ts +19 -12
- package/corpus/templates/analytics/server/lib/journey-tree.ts +24 -9
- package/corpus/templates/analytics/server/lib/onboarding-journey-cost.ts +163 -94
- package/corpus/templates/analytics/server/lib/onboarding-journey.ts +162 -74
- package/corpus/templates/assets/server/lib/org-admin.ts +20 -16
- package/corpus/templates/brain/server/lib/zoom-governance.ts +11 -6
- package/corpus/templates/clips/CHANGELOG.md +1 -0
- package/corpus/templates/clips/changelog/2026-10-16-rewind-status-says-when-it-remembers-audio.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +7 -4
- package/corpus/templates/clips/desktop/src/components/RewindCaptureStatus.tsx +25 -0
- package/corpus/templates/clips/desktop/src/lib/rewind-status.ts +6 -1
- package/corpus/templates/clips/desktop/src/shared/config.ts +3 -0
- package/corpus/templates/clips/desktop/src-tauri/src/screen_memory.rs +2 -0
- package/corpus/templates/clips/server/lib/recordings.ts +4 -19
- package/corpus/templates/community/qr/package.json +1 -1
- package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +15 -1
- package/corpus/templates/content/CHANGELOG.md +13 -0
- package/corpus/templates/content/actions/_content-space-access.ts +95 -6
- package/corpus/templates/content/actions/_database-utils.ts +20 -16
- package/corpus/templates/content/actions/_database-view-setup.ts +305 -11
- package/corpus/templates/content/actions/_github-source-freshness.ts +72 -17
- package/corpus/templates/content/actions/_github-source-metadata.ts +63 -17
- package/corpus/templates/content/actions/_github-source.ts +141 -77
- package/corpus/templates/content/actions/_property-utils.ts +3 -0
- package/corpus/templates/content/actions/check-github-connection.ts +15 -0
- package/corpus/templates/content/actions/create-github-collection.ts +180 -0
- package/corpus/templates/content/actions/disconnect-content-database-source.ts +5 -0
- package/corpus/templates/content/actions/link-github-folder.ts +104 -36
- package/corpus/templates/content/actions/refresh-content-database-source.ts +37 -4
- package/corpus/templates/content/actions/sync-github-folder.ts +23 -5
- package/corpus/templates/content/actions/update-content-database-view.ts +18 -2
- package/corpus/templates/content/actions/view-screen.ts +12 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +263 -35
- package/corpus/templates/content/app/components/editor/database/GitHubLinkDialog.tsx +938 -0
- package/corpus/templates/content/app/components/editor/database/GitHubSourcePanel.tsx +228 -0
- package/corpus/templates/content/app/components/editor/database/GitHubSourceStatusLine.tsx +561 -0
- package/corpus/templates/content/app/components/editor/database/GitHubUnlinkDialog.tsx +96 -0
- package/corpus/templates/content/app/components/editor/database/ListItemLayout.tsx +410 -0
- package/corpus/templates/content/app/components/editor/database/ListItemRow.tsx +426 -0
- package/corpus/templates/content/app/components/editor/database/ListView.tsx +136 -18
- package/corpus/templates/content/app/components/editor/database/github-availability.ts +147 -0
- package/corpus/templates/content/app/components/editor/database/github-source-text.ts +280 -0
- package/corpus/templates/content/app/components/editor/database/navigation-state.ts +3 -0
- package/corpus/templates/content/app/components/editor/database/settings.tsx +3 -3
- package/corpus/templates/content/app/components/editor/database/view-config.ts +4 -0
- package/corpus/templates/content/app/components/layout/Layout.tsx +2 -0
- package/corpus/templates/content/app/global.css +13 -0
- package/corpus/templates/content/app/hooks/content-action-refresh.ts +6 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +3 -1
- package/corpus/templates/content/app/hooks/use-github-folder.ts +199 -0
- package/corpus/templates/content/app/i18n-data.ts +186 -1
- package/corpus/templates/content/app/lib/github-link-dialog.ts +86 -0
- package/corpus/templates/content/changelog/2026-10-18-connected-ai-assistants-can-link-a-new-collection-to-a-github.md +1 -1
- package/corpus/templates/content/changelog/2026-10-18-link-a-new-collection-to-a-github-folder-from-its-sources.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-list-rows-can-be-customized.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-list-views-fit-phone-screens.md +6 -0
- package/corpus/templates/content/changelog/2026-10-21-organization-favorites-include-documents-shared-with-the-cur.md +6 -0
- package/corpus/templates/content/docs/product/capabilities/content.source.sync-policy.md +32 -7
- package/corpus/templates/content/docs/product/encyclopedia.md +3 -3
- package/corpus/templates/content/docs/product/features/content.feature.trust-your-connected-sources.md +2 -2
- package/corpus/templates/content/docs/product/roadmap.md +1 -1
- package/corpus/templates/content/parity/matrix.md +42 -42
- package/corpus/templates/content/parity/matrix.ts +14 -0
- package/corpus/templates/content/server/lib/github-source-client.ts +50 -1
- package/corpus/templates/content/shared/api.ts +21 -0
- package/corpus/templates/content/shared/database-item-layout.ts +591 -0
- package/corpus/templates/content/shared/github-source.ts +68 -0
- package/corpus/templates/design/CHANGELOG.md +1 -0
- package/corpus/templates/design/actions/connect-assets-mcp.ts +5 -9
- package/corpus/templates/design/changelog/2026-10-21-journey-canvases-accept-partial-analytics-samples-when-repla.md +6 -0
- package/corpus/templates/design/shared/journey-canvas.ts +6 -4
- package/corpus/templates/dispatch/.agents/skills/a2a-protocol/SKILL.md +16 -13
- package/corpus/templates/dispatch/CHANGELOG.md +4 -0
- package/corpus/templates/dispatch/changelog/2026-10-21-customer-ai-credit-questions-route-to-account-health-instead.md +6 -0
- package/corpus/templates/factory/server/lib/require-workspace-member.ts +5 -17
- package/corpus/templates/mail/server/lib/queued-drafts.ts +9 -4
- package/corpus/templates/slides/server/workspace-defaults.ts +12 -15
- package/package.json +1 -1
|
@@ -165,6 +165,14 @@ export function githubFolderLabel(location: GitHubFolderLocation): string {
|
|
|
165
165
|
return `${location.owner}/${location.repo}${folder ? `/${folder}` : ""}`;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
/** What people call the folder: its own name, or the repository's at the root. */
|
|
169
|
+
export function githubFolderName(
|
|
170
|
+
location: Pick<GitHubFolderLocation, "repo" | "path">,
|
|
171
|
+
): string {
|
|
172
|
+
const folder = normalizeGitHubFolderPath(location.path);
|
|
173
|
+
return folder ? folder.slice(folder.lastIndexOf("/") + 1) : location.repo;
|
|
174
|
+
}
|
|
175
|
+
|
|
168
176
|
/** Joins a folder-relative path back onto the folder for its repository path. */
|
|
169
177
|
export function githubRepoPath(folder: string, relativePath: string): string {
|
|
170
178
|
const base = normalizeGitHubFolderPath(folder);
|
|
@@ -176,6 +184,8 @@ export type GitHubSourceSkipReason = "mdx" | "too-large" | "not-text";
|
|
|
176
184
|
/**
|
|
177
185
|
* What went wrong reading a linked folder, in terms a status line can explain
|
|
178
186
|
* and fix: reconnect, regain access, restore the branch or folder, or wait.
|
|
187
|
+
* `unavailable` is GitHub not answering; `sync_failed` is a failure in
|
|
188
|
+
* Content itself, like a database error.
|
|
179
189
|
*/
|
|
180
190
|
export type GitHubSourceProblemCode =
|
|
181
191
|
| "connection"
|
|
@@ -186,6 +196,7 @@ export type GitHubSourceProblemCode =
|
|
|
186
196
|
| "rate_limited"
|
|
187
197
|
| "too_large"
|
|
188
198
|
| "unavailable"
|
|
199
|
+
| "sync_failed"
|
|
189
200
|
| "storage_unavailable";
|
|
190
201
|
|
|
191
202
|
export type GitHubSourceFileClass =
|
|
@@ -229,6 +240,31 @@ export type GitHubFieldType = Extract<
|
|
|
229
240
|
"text" | "number" | "checkbox" | "date" | "url" | "select" | "multi_select"
|
|
230
241
|
>;
|
|
231
242
|
|
|
243
|
+
/** What a folder would become if linked, from a dry run of the link. */
|
|
244
|
+
export interface GitHubFolderPreview {
|
|
245
|
+
location: GitHubFolderLocation;
|
|
246
|
+
label: string;
|
|
247
|
+
folderUrl: string;
|
|
248
|
+
commitSha: string;
|
|
249
|
+
markdownFiles: number;
|
|
250
|
+
skipped: Array<{ path: string; reason: GitHubSourceSkipReason }>;
|
|
251
|
+
/** Columns the frontmatter would become, with the type each would get. */
|
|
252
|
+
fields: Array<{ key: string; type: GitHubFieldType; examples: string[] }>;
|
|
253
|
+
sample: Array<{ path: string; title: string }>;
|
|
254
|
+
/** How many files the field types were read from. */
|
|
255
|
+
sampledFiles: number;
|
|
256
|
+
/** Image files in the folder, which pages show from Content's file storage. */
|
|
257
|
+
images: number;
|
|
258
|
+
/** Whether file storage is set up to hold those images. */
|
|
259
|
+
storageReady: boolean;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** Why a collection can't be linked: it has pages, a source, or a system role. */
|
|
263
|
+
export type GitHubLinkBlockedReason =
|
|
264
|
+
| "collection-has-rows"
|
|
265
|
+
| "already-linked"
|
|
266
|
+
| "system-collection";
|
|
267
|
+
|
|
232
268
|
const SELECT_KEY_RE =
|
|
233
269
|
/^(status|state|stage|category|type|kind|section|level|priority|visibility|difficulty|audience)$/i;
|
|
234
270
|
const DATE_RE =
|
|
@@ -299,6 +335,38 @@ export function inferGitHubFieldType(
|
|
|
299
335
|
return "text";
|
|
300
336
|
}
|
|
301
337
|
|
|
338
|
+
const GITHUB_EXAMPLE_LENGTH = 80;
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Up to three example values for a column, as the link preview shows them:
|
|
342
|
+
* blank values and empty lists are skipped, and a long value ends in "…".
|
|
343
|
+
*/
|
|
344
|
+
export function githubFieldExamples(values: readonly unknown[]): string[] {
|
|
345
|
+
const examples: string[] = [];
|
|
346
|
+
for (const value of values) {
|
|
347
|
+
if (examples.length === 3) break;
|
|
348
|
+
if (value === null || value === undefined) continue;
|
|
349
|
+
const text = (
|
|
350
|
+
typeof value === "string"
|
|
351
|
+
? value
|
|
352
|
+
: Array.isArray(value)
|
|
353
|
+
? value
|
|
354
|
+
.map((entry) =>
|
|
355
|
+
typeof entry === "string" ? entry : JSON.stringify(entry),
|
|
356
|
+
)
|
|
357
|
+
.join(", ")
|
|
358
|
+
: JSON.stringify(value)
|
|
359
|
+
).trim();
|
|
360
|
+
if (!text) continue;
|
|
361
|
+
examples.push(
|
|
362
|
+
text.length > GITHUB_EXAMPLE_LENGTH
|
|
363
|
+
? `${text.slice(0, GITHUB_EXAMPLE_LENGTH - 1).trimEnd()}…`
|
|
364
|
+
: text,
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
return examples;
|
|
368
|
+
}
|
|
369
|
+
|
|
302
370
|
/** Whether a frontmatter value can be stored in a column of this type unchanged. */
|
|
303
371
|
export function fitsGitHubFieldType(
|
|
304
372
|
type: GitHubFieldType,
|
|
@@ -7,6 +7,7 @@ from the command menu (Cmd+K → "What's new") or from Settings.
|
|
|
7
7
|
|
|
8
8
|
### Fixed
|
|
9
9
|
|
|
10
|
+
- Journey canvases accept partial Analytics samples when replay counts are unknown
|
|
10
11
|
- Design generation now counts file renames and type changes as saved output.
|
|
11
12
|
- Design reports uncertain generation outcomes clearly and confirms completed file deletions
|
|
12
13
|
- Generation offers a retry when a completed run saved no design files
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core/action";
|
|
2
|
-
import { getDbExec } from "@agent-native/core/db";
|
|
3
2
|
import { fetchOrgApps } from "@agent-native/core/mcp";
|
|
4
3
|
import {
|
|
5
4
|
addFirstPartyRemoteServer,
|
|
@@ -7,7 +6,10 @@ import {
|
|
|
7
6
|
listRemoteServers,
|
|
8
7
|
removeRemoteServer,
|
|
9
8
|
} from "@agent-native/core/mcp-client";
|
|
10
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
readOrgMemberRole,
|
|
11
|
+
refreshMcpManagerForPrincipal,
|
|
12
|
+
} from "@agent-native/core/server";
|
|
11
13
|
import {
|
|
12
14
|
getRequestOrgId,
|
|
13
15
|
getRequestUserEmail,
|
|
@@ -41,13 +43,7 @@ async function listAssetsServers(orgId: string) {
|
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
async function assertCanManageOrgMcp(orgId: string, userEmail: string) {
|
|
44
|
-
const
|
|
45
|
-
sql: `SELECT role FROM org_members WHERE org_id = $1 AND LOWER(email) = $2 LIMIT 1`,
|
|
46
|
-
args: [orgId, userEmail.toLowerCase()],
|
|
47
|
-
});
|
|
48
|
-
const role = String(
|
|
49
|
-
(result.rows[0] as { role?: unknown } | undefined)?.role,
|
|
50
|
-
).toLowerCase();
|
|
46
|
+
const role = await readOrgMemberRole(orgId, userEmail);
|
|
51
47
|
if (role !== "owner" && role !== "admin") {
|
|
52
48
|
throw new Error(
|
|
53
49
|
"Only organization owners and admins can connect org-scoped MCP servers.",
|
|
@@ -147,7 +147,7 @@ export const journeyExampleSchema = z.object({
|
|
|
147
147
|
.describe(
|
|
148
148
|
"SHA-256 hash used only to compare anonymous identity when available.",
|
|
149
149
|
),
|
|
150
|
-
recordingId: z.string().max(256).nullable(),
|
|
150
|
+
recordingId: z.string().max(256).nullable().optional(),
|
|
151
151
|
ts: isoTimestamp,
|
|
152
152
|
offsetMs: z.number().min(0).nullable(),
|
|
153
153
|
viewport: z.object({ width: pixels, height: pixels }).nullable(),
|
|
@@ -298,7 +298,7 @@ export const journeyTreeSchema = z
|
|
|
298
298
|
.describe("Per-app root denominators for separate app-band layouts."),
|
|
299
299
|
coverage: z.object({
|
|
300
300
|
sessionsWithEvents: count,
|
|
301
|
-
sessionsWithReplay: count,
|
|
301
|
+
sessionsWithReplay: count.nullable(),
|
|
302
302
|
truncated: z.boolean(),
|
|
303
303
|
}),
|
|
304
304
|
nodes: z.array(journeyNodeSchema).min(1).max(MAX_JOURNEY_NODES),
|
|
@@ -1406,7 +1406,7 @@ export interface PlannedScreen {
|
|
|
1406
1406
|
provenance?: {
|
|
1407
1407
|
eventAt: string;
|
|
1408
1408
|
dateLabel: string;
|
|
1409
|
-
recordingId
|
|
1409
|
+
recordingId?: string | null;
|
|
1410
1410
|
recordingStartedAt?: string;
|
|
1411
1411
|
offsetMs: number | null;
|
|
1412
1412
|
offsetIsObserved: boolean;
|
|
@@ -2315,7 +2315,9 @@ export function planJourneyCanvas(
|
|
|
2315
2315
|
candidate.caption.evidenceAt !== undefined,
|
|
2316
2316
|
messages,
|
|
2317
2317
|
),
|
|
2318
|
-
|
|
2318
|
+
...(candidateExample.recordingId !== undefined
|
|
2319
|
+
? { recordingId: candidateExample.recordingId }
|
|
2320
|
+
: {}),
|
|
2319
2321
|
...(candidate.recordingStartedAt
|
|
2320
2322
|
? {
|
|
2321
2323
|
recordingStartedAt: utcTimestamp(
|
|
@@ -144,14 +144,17 @@ The framework supports two credential scopes:
|
|
|
144
144
|
| Secret | Scope | How it is set |
|
|
145
145
|
| --- | --- | --- |
|
|
146
146
|
| `A2A_SECRET` | User identity across a deployment | env var, **never auto-generated** |
|
|
147
|
-
| org `a2a_secret` | Organization principal
|
|
147
|
+
| org `a2a_secret` | Organization principal, with optional current-member delegation | auto-generated on org creation; Team page UI |
|
|
148
148
|
|
|
149
|
-
An org-secret JWT proves only that the caller holds the organization's
|
|
150
|
-
credential. Its `sub` or email claims are ignored; do not use it to read
|
|
149
|
+
An ordinary org-secret JWT proves only that the caller holds the organization's
|
|
150
|
+
shared credential. Its `sub` or email claims are ignored; do not use it to read
|
|
151
151
|
person-owned data, claim a user's task, invoke a user-scoped action, or carry a
|
|
152
|
-
human approval.
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
human approval. A separate `agent-native-org-member-delegation` assertion may
|
|
153
|
+
carry the identity of any current receiver-org member to a caller holding that
|
|
154
|
+
shared secret, but only when the receiver's owner enables the setting, the org
|
|
155
|
+
secret differs from `A2A_SECRET`, and the audience and short expiry verify. The
|
|
156
|
+
receiver checks the live roster, and delegated org actions are always capped at
|
|
157
|
+
`member`. MCP requests remain organization-scoped.
|
|
155
158
|
|
|
156
159
|
Organization IDs are local to each app's database. For calls to a peer app,
|
|
157
160
|
bind the token to the trusted `org_domain` and the peer's canonical audience;
|
|
@@ -161,16 +164,16 @@ ID. An ID-only token is valid only when both sides intentionally share the same
|
|
|
161
164
|
local organization ID, such as a credential scoped to one local database.
|
|
162
165
|
|
|
163
166
|
The org secret is managed at **Team page → A2A secret** (owner only), where it
|
|
164
|
-
can be revealed, copied, regenerated, and pushed to discovered peers.
|
|
165
|
-
|
|
167
|
+
can be revealed, copied, regenerated, and pushed to discovered peers. Its
|
|
168
|
+
member-identity delegation setting is owner-only and defaults off.
|
|
166
169
|
|
|
167
170
|
`A2A_SECRET` is the deploy-level path. `workspace-deploy` refuses a production
|
|
168
171
|
workspace deploy without it and prints the generator command. Peers that must
|
|
169
|
-
trust user
|
|
170
|
-
different from every org `a2a_secret`; a shared value cannot
|
|
171
|
-
user assertion from an organization credential. An
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
trust deployment-secret user assertions need the *same* value on both sides.
|
|
173
|
+
Keep it different from every org `a2a_secret`; a shared value cannot
|
|
174
|
+
distinguish a deployment user assertion from an organization credential. An
|
|
175
|
+
org secret equal to `A2A_SECRET` is verified as an organization principal and
|
|
176
|
+
reaches the caller as the typed
|
|
174
177
|
`permanent_precondition` error ("no verified user"): an admin/ops fix, never a
|
|
175
178
|
retry. Mismatched `A2A_SECRET` values are rejected with `401`
|
|
176
179
|
(`a2a_auth_rejected`).
|
|
@@ -5,6 +5,10 @@ time from the command menu (Cmd+K → "What's new").
|
|
|
5
5
|
|
|
6
6
|
## 2026-10-21
|
|
7
7
|
|
|
8
|
+
### Improved
|
|
9
|
+
|
|
10
|
+
- Customer AI credit questions route to account health instead of workspace spend metrics.
|
|
11
|
+
|
|
8
12
|
### Fixed
|
|
9
13
|
|
|
10
14
|
- Workspace apps load reliably with organization sign-in, and failures identify the request and status
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import type { ActionRunContext } from "@agent-native/core/action";
|
|
2
|
-
import {
|
|
2
|
+
import { resolveOrgIdForEmail } from "@agent-native/core/org";
|
|
3
|
+
import { readOrgMemberRole } from "@agent-native/core/server";
|
|
3
4
|
import {
|
|
4
5
|
getRequestOrgId,
|
|
5
6
|
getRequestUserEmail,
|
|
6
7
|
} from "@agent-native/core/server/request-context";
|
|
7
|
-
import { and, eq, sql } from "drizzle-orm";
|
|
8
|
-
|
|
9
|
-
import { getDb } from "../db/index.js";
|
|
10
8
|
|
|
11
9
|
export type WorkspaceMemberIdentity = {
|
|
12
10
|
userEmail: string;
|
|
@@ -38,18 +36,8 @@ export async function requireWorkspaceMember(
|
|
|
38
36
|
const orgId = requestOrgId || (await resolveOrgIdForEmail(userEmail));
|
|
39
37
|
if (!orgId) throw new Error("Active workspace organization required");
|
|
40
38
|
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
.from(orgMembers)
|
|
44
|
-
.where(
|
|
45
|
-
and(
|
|
46
|
-
eq(orgMembers.orgId, orgId),
|
|
47
|
-
eq(sql`lower(${orgMembers.email})`, userEmail),
|
|
48
|
-
),
|
|
49
|
-
)
|
|
50
|
-
.limit(1);
|
|
51
|
-
|
|
52
|
-
if (!rows[0]) throw new Error("Workspace membership required");
|
|
39
|
+
const role = await readOrgMemberRole(orgId, userEmail);
|
|
40
|
+
if (!role) throw new Error("Workspace membership required");
|
|
53
41
|
|
|
54
|
-
return { userEmail, orgId, role
|
|
42
|
+
return { userEmail, orgId, role };
|
|
55
43
|
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
getAppProductionUrl,
|
|
6
6
|
getRequestOrgId,
|
|
7
7
|
getRequestUserEmail,
|
|
8
|
+
readOrgMemberRole,
|
|
8
9
|
withConfiguredAppBasePath,
|
|
9
10
|
} from "@agent-native/core/server";
|
|
10
11
|
import { getUserSetting } from "@agent-native/core/settings";
|
|
@@ -107,9 +108,9 @@ export function buildQueuedDraftUrl(id: string): string {
|
|
|
107
108
|
async function getMembership(
|
|
108
109
|
orgId: string,
|
|
109
110
|
email: string,
|
|
110
|
-
): Promise<{ email: string
|
|
111
|
+
): Promise<{ email: string } | null> {
|
|
111
112
|
const [row] = await getDb()
|
|
112
|
-
.select({ email: orgMembers.email
|
|
113
|
+
.select({ email: orgMembers.email })
|
|
113
114
|
.from(orgMembers)
|
|
114
115
|
.where(
|
|
115
116
|
and(
|
|
@@ -119,7 +120,7 @@ async function getMembership(
|
|
|
119
120
|
)
|
|
120
121
|
.limit(1);
|
|
121
122
|
if (!row) return null;
|
|
122
|
-
return { email: row.email
|
|
123
|
+
return { email: row.email };
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
export async function requireQueueContext(): Promise<QueueContext> {
|
|
@@ -136,11 +137,15 @@ export async function requireQueueContext(): Promise<QueueContext> {
|
|
|
136
137
|
if (!membership) {
|
|
137
138
|
throw new Error("Only members of this organization can queue drafts.");
|
|
138
139
|
}
|
|
140
|
+
const role = await readOrgMemberRole(orgId, userEmail);
|
|
141
|
+
if (!role) {
|
|
142
|
+
throw new Error("Only members of this organization can queue drafts.");
|
|
143
|
+
}
|
|
139
144
|
|
|
140
145
|
return {
|
|
141
146
|
userEmail: normalizeEmail(membership.email),
|
|
142
147
|
orgId,
|
|
143
|
-
role
|
|
148
|
+
role,
|
|
144
149
|
};
|
|
145
150
|
}
|
|
146
151
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { fail } from "@agent-native/core/action";
|
|
2
|
-
import { canManageOrg,
|
|
2
|
+
import { canManageOrg, type OrgRole } from "@agent-native/core/org";
|
|
3
|
+
import { readOrgMemberRole } from "@agent-native/core/server";
|
|
3
4
|
import {
|
|
5
|
+
getRequestContext,
|
|
4
6
|
getRequestOrgId,
|
|
5
7
|
getRequestUserEmail,
|
|
6
8
|
} from "@agent-native/core/server/request-context";
|
|
@@ -41,20 +43,15 @@ export async function getCallerOrgRole(): Promise<OrgRole | null> {
|
|
|
41
43
|
const orgId = getRequestOrgId();
|
|
42
44
|
const email = getRequestUserEmail();
|
|
43
45
|
if (!orgId || !email) return null;
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
.limit(1);
|
|
54
|
-
const role = rows[0]?.role;
|
|
55
|
-
return role === "owner" || role === "admin" || role === "member"
|
|
56
|
-
? role
|
|
57
|
-
: null;
|
|
46
|
+
const request = getRequestContext();
|
|
47
|
+
if (
|
|
48
|
+
request?.a2aIdentityAssurance === "org-member" &&
|
|
49
|
+
(request.orgId?.trim() !== orgId.trim() ||
|
|
50
|
+
request.userEmail?.trim().toLowerCase() !== email.trim().toLowerCase())
|
|
51
|
+
) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return readOrgMemberRole(orgId, email);
|
|
58
55
|
}
|
|
59
56
|
|
|
60
57
|
export async function canManageWorkspaceDefaults(): Promise<boolean> {
|
package/package.json
CHANGED