@agent-native/core 0.201.0 → 0.201.1
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/dist/a2a/handlers.js +96 -44
- package/dist/a2a/organization-identity.d.ts +9 -0
- package/dist/a2a/organization-identity.js +34 -0
- package/dist/a2a/server.d.ts +13 -7
- package/dist/a2a/server.js +187 -88
- package/dist/a2a/task-store.d.ts +7 -2
- package/dist/a2a/task-store.js +23 -8
- package/dist/a2a-claims.js +12 -2
- package/dist/app-config/mcp.d.ts +4 -0
- package/dist/app-config/mcp.js +7 -0
- package/dist/app-config/schema.d.ts +3 -0
- package/dist/app-config/schema.js +2 -0
- package/dist/automation/index.d.ts +1 -1
- package/dist/client/route-chunk-recovery.js +75 -11
- package/dist/client/route-warmup.d.ts +2 -0
- package/dist/client/route-warmup.js +3 -0
- package/dist/client/vite-dev-recovery-script.js +85 -17
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.js +3 -1
- package/dist/feature-flags/a2a-action-route.js +3 -4
- package/dist/mcp/build-server.d.ts +4 -0
- package/dist/mcp/build-server.js +152 -43
- package/dist/mcp/embed-app.js +5 -5
- package/dist/mcp/server.d.ts +0 -18
- package/dist/mcp/server.js +99 -9
- package/dist/onboarding/app-profile.js +2 -1
- package/dist/onboarding/workspace-services.js +3 -0
- package/dist/org/context.d.ts +13 -0
- package/dist/org/context.js +60 -0
- package/dist/org/handlers.d.ts +1 -1
- package/dist/org/index.d.ts +1 -1
- package/dist/org/membership.d.ts +6 -0
- package/dist/org/membership.js +23 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
- package/dist/provider-api/actions/provider-api.d.ts +6 -6
- package/dist/secrets/routes.d.ts +11 -11
- package/dist/server/action-routes.js +12 -5
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/builder-browser.d.ts +4 -2
- package/dist/server/builder-browser.js +147 -9
- package/dist/server/core-routes-plugin.js +13 -6
- package/dist/server/entry-server.js +43 -2
- package/dist/server/sentry.js +9 -1
- package/dist/server/ssr-handler.d.ts +1 -1
- package/dist/server/ssr-handler.js +3 -7
- package/dist/shared/cache-control.d.ts +4 -1
- package/dist/shared/cache-control.js +11 -6
- package/dist/shared/route-chunk-recovery-bootstrap.d.ts +7 -0
- package/dist/shared/route-chunk-recovery-bootstrap.js +38 -0
- package/package.json +5 -3
package/dist/a2a/handlers.js
CHANGED
|
@@ -5,14 +5,14 @@ import { trackingIdentityProperties } from "../observability/tracking-identity.j
|
|
|
5
5
|
import { findWorkspaceDispatchAgent } from "../server/agent-discovery.js";
|
|
6
6
|
import { withConfiguredAppBasePath } from "../server/app-base-path.js";
|
|
7
7
|
import { getOrigin, isConfiguredAppOrigin } from "../server/google-oauth.js";
|
|
8
|
+
import { markExplicitPersonalOrgScope } from "../server/request-context.js";
|
|
8
9
|
import { fireInternalDispatch } from "../server/self-dispatch.js";
|
|
9
10
|
import { agentChat } from "../shared/agent-chat.js";
|
|
10
11
|
import { track } from "../tracking/registry.js";
|
|
11
12
|
import { hasConfiguredA2ASecret, isA2AProductionRuntime, } from "./auth-policy.js";
|
|
12
13
|
import { callAction } from "./client.js";
|
|
13
14
|
import { sanitizeA2ACorrelationMetadata } from "./correlation.js";
|
|
14
|
-
import { createTask, createOrReuseTask, getTask, getTaskOwnership, updateTask, claimA2ATaskForProcessing, getA2ATaskDispatchState, failStuckA2ATask, failStuckQueuedA2ATask, settleProcessingA2ATask, touchQueuedA2ATaskDispatch, touchProcessingA2ATask, pauseProcessingA2ATask, MAX_A2A_IDEMPOTENCY_KEY_CHARS, A2A_PERSONAL_OWNER_SCOPE, } from "./task-store.js";
|
|
15
|
-
const getA2ASecretByDomain = (...args) => import("../org/context.js").then(({ getA2ASecretByDomain }) => getA2ASecretByDomain(...args));
|
|
15
|
+
import { createTask, createOrReuseTask, getTask, getTaskOwnership, updateTask, claimA2ATaskForProcessing, getA2ATaskDispatchState, failStuckA2ATask, failStuckQueuedA2ATask, settleProcessingA2ATask, touchQueuedA2ATaskDispatch, touchProcessingA2ATask, pauseProcessingA2ATask, MAX_A2A_IDEMPOTENCY_KEY_CHARS, A2A_PERSONAL_OWNER_SCOPE, A2A_ORG_ID_OWNER_SCOPE_PREFIX, } from "./task-store.js";
|
|
16
16
|
const A2A_PROCESS_TASK_PATH = "/_agent-native/a2a/_process-task";
|
|
17
17
|
const PORTABLE_FALLBACK_HANDOFF_TIMEOUT_MS = 1_000;
|
|
18
18
|
const A2A_QUEUED_DISPATCH_STUCK_AFTER_MS = 10_000;
|
|
@@ -94,12 +94,15 @@ async function trustedSourceContext(value, event) {
|
|
|
94
94
|
if (!dispatch)
|
|
95
95
|
return undefined;
|
|
96
96
|
const orgDomain = event?.context?.__a2aOrgDomain;
|
|
97
|
+
const verifiedOrgId = event?.context?.__a2aVerifiedOrgId;
|
|
97
98
|
let orgSecret;
|
|
98
99
|
if (orgDomain) {
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
const { resolveA2AOrganizationCredentialsByDomain } = await import("../org/context.js");
|
|
101
|
+
const organization = await resolveA2AOrganizationCredentialsByDomain(orgDomain);
|
|
102
|
+
if (!verifiedOrgId || organization?.orgId !== verifiedOrgId) {
|
|
103
|
+
return undefined;
|
|
101
104
|
}
|
|
102
|
-
|
|
105
|
+
orgSecret = organization.secret;
|
|
103
106
|
}
|
|
104
107
|
try {
|
|
105
108
|
const result = await callAction(dispatch.url, "resolve-integration-source-context", { integrationTaskId: reference.integrationTaskId }, {
|
|
@@ -252,6 +255,9 @@ export async function processA2ATaskFromQueue(taskId, config, event) {
|
|
|
252
255
|
const processorMeta = (meta.__a2a_processor ?? {});
|
|
253
256
|
const verifiedEmail = processorMeta.verifiedEmail;
|
|
254
257
|
const orgDomainHint = processorMeta.orgDomainHint;
|
|
258
|
+
const verifiedOrgId = typeof processorMeta.verifiedOrgId === "string"
|
|
259
|
+
? processorMeta.verifiedOrgId.trim()
|
|
260
|
+
: undefined;
|
|
255
261
|
const requestOrigin = requestOriginFromMetadata(processorMeta) ?? requestOriginFromEvent(event);
|
|
256
262
|
const contextId = processorMeta.contextId ?? undefined;
|
|
257
263
|
const callerMetadata = processorMeta.callerMetadata ?? undefined;
|
|
@@ -259,7 +265,17 @@ export async function processA2ATaskFromQueue(taskId, config, event) {
|
|
|
259
265
|
? processorMeta.approvedActions
|
|
260
266
|
: undefined;
|
|
261
267
|
const sourceContext = processorMeta.sourceContext;
|
|
262
|
-
const resolvedOrgId =
|
|
268
|
+
const resolvedOrgId = verifiedOrgId || undefined;
|
|
269
|
+
if (event?.context) {
|
|
270
|
+
if (verifiedEmail)
|
|
271
|
+
event.context.__a2aVerifiedEmail = verifiedEmail;
|
|
272
|
+
if (orgDomainHint)
|
|
273
|
+
event.context.__a2aOrgDomain = orgDomainHint;
|
|
274
|
+
if (verifiedOrgId)
|
|
275
|
+
event.context.__a2aVerifiedOrgId = verifiedOrgId;
|
|
276
|
+
if (verifiedEmail && !resolvedOrgId)
|
|
277
|
+
markExplicitPersonalOrgScope(event);
|
|
278
|
+
}
|
|
263
279
|
const { runWithRequestContext } = await import("../server/request-context.js");
|
|
264
280
|
const heartbeat = setInterval(() => {
|
|
265
281
|
touchProcessingA2ATask(taskId).catch((err) => console.error("[a2a] Failed to heartbeat async task:", err));
|
|
@@ -268,7 +284,11 @@ export async function processA2ATaskFromQueue(taskId, config, event) {
|
|
|
268
284
|
try {
|
|
269
285
|
await runWithRequestContext({
|
|
270
286
|
userEmail: verifiedEmail,
|
|
271
|
-
|
|
287
|
+
...(resolvedOrgId
|
|
288
|
+
? { orgId: resolvedOrgId }
|
|
289
|
+
: verifiedEmail
|
|
290
|
+
? { orgScope: "personal" }
|
|
291
|
+
: {}),
|
|
272
292
|
...(requestOrigin ? { requestOrigin } : {}),
|
|
273
293
|
}, () => runHandlerAndPersist(taskId, message, config, contextId, callerMetadata, event, approvedActions, sourceContext));
|
|
274
294
|
}
|
|
@@ -378,38 +398,21 @@ function makeHandlerContext(taskId, contextId, metadata, event, approvedActions,
|
|
|
378
398
|
async function withA2ARequestContext(metadata, event, fn) {
|
|
379
399
|
const { runWithRequestContext } = await import("../server/request-context.js");
|
|
380
400
|
const verifiedEmail = event?.context?.__a2aVerifiedEmail ?? undefined;
|
|
381
|
-
const
|
|
382
|
-
const resolvedOrgId = await resolveVerifiedA2AOrgId(verifiedEmail, orgDomain);
|
|
401
|
+
const verifiedOrgId = event?.context?.__a2aVerifiedOrgId ?? undefined;
|
|
383
402
|
const requestOrigin = requestOriginForContext(metadata, event);
|
|
403
|
+
if (event?.context && verifiedEmail && !verifiedOrgId) {
|
|
404
|
+
markExplicitPersonalOrgScope(event);
|
|
405
|
+
}
|
|
384
406
|
return runWithRequestContext({
|
|
385
407
|
userEmail: verifiedEmail,
|
|
386
|
-
|
|
408
|
+
...(verifiedOrgId
|
|
409
|
+
? { orgId: verifiedOrgId }
|
|
410
|
+
: verifiedEmail
|
|
411
|
+
? { orgScope: "personal" }
|
|
412
|
+
: {}),
|
|
387
413
|
...(requestOrigin ? { requestOrigin } : {}),
|
|
388
414
|
}, fn);
|
|
389
415
|
}
|
|
390
|
-
async function resolveVerifiedA2AOrgId(verifiedEmail, verifiedOrgDomain) {
|
|
391
|
-
if (verifiedOrgDomain) {
|
|
392
|
-
try {
|
|
393
|
-
const { resolveOrgByDomain } = await import("../org/context.js");
|
|
394
|
-
const org = await resolveOrgByDomain(verifiedOrgDomain);
|
|
395
|
-
if (org)
|
|
396
|
-
return org.orgId;
|
|
397
|
-
}
|
|
398
|
-
catch {
|
|
399
|
-
// Org tables may not exist — continue without org context
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
if (verifiedEmail) {
|
|
403
|
-
try {
|
|
404
|
-
const { resolveOrgIdForEmail } = await import("../org/context.js");
|
|
405
|
-
return (await resolveOrgIdForEmail(verifiedEmail)) ?? undefined;
|
|
406
|
-
}
|
|
407
|
-
catch {
|
|
408
|
-
// Org tables may not exist — continue without org context
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
return undefined;
|
|
412
|
-
}
|
|
413
416
|
async function runHandlerAndPersist(taskId, message, config, contextId, metadata, event, approvedActions, sourceContext) {
|
|
414
417
|
const { context, artifacts } = makeHandlerContext(taskId, contextId, metadata, event, approvedActions, sourceContext);
|
|
415
418
|
try {
|
|
@@ -464,15 +467,24 @@ function taskFailureMessage(error, fallback) {
|
|
|
464
467
|
}
|
|
465
468
|
function verifiedTaskOwner(event) {
|
|
466
469
|
const ownerEmail = event?.context?.__a2aVerifiedEmail ?? null;
|
|
470
|
+
const verifiedOrgId = event?.context?.__a2aVerifiedOrgId
|
|
471
|
+
?.trim()
|
|
472
|
+
.toLowerCase() ?? "";
|
|
467
473
|
return {
|
|
468
474
|
ownerEmail,
|
|
469
475
|
ownerScope: ownerEmail
|
|
470
|
-
?
|
|
471
|
-
|
|
472
|
-
|
|
476
|
+
? verifiedOrgId
|
|
477
|
+
? `${A2A_ORG_ID_OWNER_SCOPE_PREFIX}${verifiedOrgId}`
|
|
478
|
+
: A2A_PERSONAL_OWNER_SCOPE
|
|
473
479
|
: null,
|
|
474
480
|
};
|
|
475
481
|
}
|
|
482
|
+
function hasUnboundVerifiedOrgIdentity(event) {
|
|
483
|
+
const verifiedEmail = event?.context?.__a2aVerifiedEmail?.trim() ?? "";
|
|
484
|
+
const verifiedOrgDomain = event?.context?.__a2aOrgDomain?.trim() ?? "";
|
|
485
|
+
const verifiedOrgId = event?.context?.__a2aVerifiedOrgId?.trim() ?? "";
|
|
486
|
+
return Boolean(verifiedEmail && verifiedOrgDomain && !verifiedOrgId);
|
|
487
|
+
}
|
|
476
488
|
async function handleSend(params, config, event) {
|
|
477
489
|
const message = params.message;
|
|
478
490
|
if (!message || !message.role || !Array.isArray(message.parts)) {
|
|
@@ -481,6 +493,12 @@ async function handleSend(params, config, event) {
|
|
|
481
493
|
_id: 0,
|
|
482
494
|
};
|
|
483
495
|
}
|
|
496
|
+
if (hasUnboundVerifiedOrgIdentity(event)) {
|
|
497
|
+
return {
|
|
498
|
+
...jsonRpcError(0, -32001, "A stable verified organization identity is required"),
|
|
499
|
+
_id: 0,
|
|
500
|
+
};
|
|
501
|
+
}
|
|
484
502
|
const contextId = params.contextId;
|
|
485
503
|
const metadata = params.metadata;
|
|
486
504
|
const approvedActions = trustedApprovedActions(params.approvedActions, event);
|
|
@@ -538,6 +556,9 @@ async function handleSend(params, config, event) {
|
|
|
538
556
|
__a2a_processor: {
|
|
539
557
|
verifiedEmail,
|
|
540
558
|
orgDomainHint,
|
|
559
|
+
...(typeof event?.context?.__a2aVerifiedOrgId === "string"
|
|
560
|
+
? { verifiedOrgId: event.context.__a2aVerifiedOrgId }
|
|
561
|
+
: {}),
|
|
541
562
|
...(requestOrigin ? { requestOrigin } : {}),
|
|
542
563
|
contextId: contextId ?? null,
|
|
543
564
|
callerMetadata: safeMetadata ?? null,
|
|
@@ -638,6 +659,11 @@ async function handleStream(params, config, res, event) {
|
|
|
638
659
|
res.end();
|
|
639
660
|
return;
|
|
640
661
|
}
|
|
662
|
+
if (hasUnboundVerifiedOrgIdentity(event)) {
|
|
663
|
+
res.write(`data: ${JSON.stringify(jsonRpcError(0, -32001, "A stable verified organization identity is required"))}\n\n`);
|
|
664
|
+
res.end();
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
641
667
|
const contextId = params.contextId;
|
|
642
668
|
const metadata = params.metadata;
|
|
643
669
|
const approvedActions = trustedApprovedActions(params.approvedActions, event);
|
|
@@ -754,17 +780,39 @@ function authorizeTaskAccess(taskOwnerEmail, taskOwnerScope, event, config) {
|
|
|
754
780
|
if (verifiedEmail.toLowerCase() !== taskOwnerEmail.toLowerCase()) {
|
|
755
781
|
return jsonRpcError(0, -32001, "Task not found");
|
|
756
782
|
}
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
783
|
+
const storedScope = taskOwnerScope?.trim().toLowerCase() ?? "";
|
|
784
|
+
if (!storedScope) {
|
|
785
|
+
// Legacy empty scopes cannot distinguish personal tasks from org tasks.
|
|
786
|
+
return jsonRpcError(0, -32001, "Task not found");
|
|
787
|
+
}
|
|
788
|
+
const verifiedOrgId = event?.context?.__a2aVerifiedOrgId
|
|
789
|
+
?.trim()
|
|
790
|
+
.toLowerCase() ?? "";
|
|
791
|
+
const verifiedOrgDomain = event?.context?.__a2aOrgDomain?.trim() ?? "";
|
|
792
|
+
if (storedScope.startsWith(A2A_ORG_ID_OWNER_SCOPE_PREFIX)) {
|
|
793
|
+
if (!verifiedOrgId ||
|
|
794
|
+
storedScope !== `${A2A_ORG_ID_OWNER_SCOPE_PREFIX}${verifiedOrgId}`) {
|
|
762
795
|
return jsonRpcError(0, -32001, "Task not found");
|
|
763
796
|
}
|
|
764
797
|
}
|
|
798
|
+
else if (storedScope === A2A_PERSONAL_OWNER_SCOPE &&
|
|
799
|
+
!verifiedOrgId &&
|
|
800
|
+
!verifiedOrgDomain) {
|
|
801
|
+
// A verified identity with no organization is in its personal scope.
|
|
802
|
+
}
|
|
803
|
+
else {
|
|
804
|
+
// Legacy domain scopes cannot be safely rebound after a domain change.
|
|
805
|
+
return jsonRpcError(0, -32001, "Task not found");
|
|
806
|
+
}
|
|
765
807
|
}
|
|
766
808
|
return null;
|
|
767
809
|
}
|
|
810
|
+
function taskAccessScope(ownership, event) {
|
|
811
|
+
const verifiedEmail = event?.context?.__a2aVerifiedEmail?.trim() ?? "";
|
|
812
|
+
if (!ownership.ownerEmail || !verifiedEmail)
|
|
813
|
+
return undefined;
|
|
814
|
+
return { ownerEmail: verifiedEmail, ownerScope: ownership.ownerScope };
|
|
815
|
+
}
|
|
768
816
|
async function handleGet(params, event, config) {
|
|
769
817
|
const id = params.id;
|
|
770
818
|
if (!id) {
|
|
@@ -774,7 +822,8 @@ async function handleGet(params, event, config) {
|
|
|
774
822
|
const denied = authorizeTaskAccess(ownership.ownerEmail, ownership.ownerScope, event, config);
|
|
775
823
|
if (denied)
|
|
776
824
|
return denied;
|
|
777
|
-
const
|
|
825
|
+
const accessScope = taskAccessScope(ownership, event);
|
|
826
|
+
const task = await getTask(id, accessScope);
|
|
778
827
|
if (!task) {
|
|
779
828
|
return jsonRpcError(0, -32001, "Task not found");
|
|
780
829
|
}
|
|
@@ -783,7 +832,7 @@ async function handleGet(params, event, config) {
|
|
|
783
832
|
return false;
|
|
784
833
|
});
|
|
785
834
|
if (taskChanged) {
|
|
786
|
-
const updated = await getTask(id);
|
|
835
|
+
const updated = await getTask(id, accessScope);
|
|
787
836
|
if (updated)
|
|
788
837
|
return jsonRpcResult(0, sanitizeTaskForResponse(updated));
|
|
789
838
|
}
|
|
@@ -837,7 +886,7 @@ async function handleCancel(params, event, config) {
|
|
|
837
886
|
const denied = authorizeTaskAccess(ownership.ownerEmail, ownership.ownerScope, event, config);
|
|
838
887
|
if (denied)
|
|
839
888
|
return denied;
|
|
840
|
-
const task = await updateTask(id, { state: "canceled" });
|
|
889
|
+
const task = await updateTask(id, { state: "canceled" }, taskAccessScope(ownership, event));
|
|
841
890
|
if (!task) {
|
|
842
891
|
return jsonRpcError(0, -32001, "Task not found");
|
|
843
892
|
}
|
|
@@ -869,6 +918,9 @@ async function handleInvokeReadOnlyAction(params, event, config) {
|
|
|
869
918
|
event?.context?.__a2aAudienceVerified !== true) {
|
|
870
919
|
return jsonRpcError(0, -32001, "A verified, audience-bound user identity is required for direct action invocation");
|
|
871
920
|
}
|
|
921
|
+
if (hasUnboundVerifiedOrgIdentity(event)) {
|
|
922
|
+
return jsonRpcError(0, -32001, "A stable verified organization identity is required for direct action invocation");
|
|
923
|
+
}
|
|
872
924
|
if (!config.executeReadOnlyAction) {
|
|
873
925
|
return jsonRpcError(0, -32601, "Direct action invocation not supported");
|
|
874
926
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface VerifiedA2AOrganizationIdentity {
|
|
2
|
+
orgId: string;
|
|
3
|
+
orgDomain: string | null;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Bind shared-secret organization claims to this receiver's organization
|
|
7
|
+
* metadata before they can become request or action scope.
|
|
8
|
+
*/
|
|
9
|
+
export declare function verifyA2AOrganizationIdentity(payload: Record<string, unknown>): Promise<VerifiedA2AOrganizationIdentity | null | undefined>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bind shared-secret organization claims to this receiver's organization
|
|
3
|
+
* metadata before they can become request or action scope.
|
|
4
|
+
*/
|
|
5
|
+
export async function verifyA2AOrganizationIdentity(payload) {
|
|
6
|
+
const hasOrgId = Object.prototype.hasOwnProperty.call(payload, "org_id");
|
|
7
|
+
const hasOrgDomain = Object.prototype.hasOwnProperty.call(payload, "org_domain");
|
|
8
|
+
const rawOrgId = payload.org_id;
|
|
9
|
+
const rawOrgDomain = payload.org_domain;
|
|
10
|
+
if (!hasOrgId && !hasOrgDomain)
|
|
11
|
+
return undefined;
|
|
12
|
+
if ((hasOrgId &&
|
|
13
|
+
rawOrgId !== null &&
|
|
14
|
+
(typeof rawOrgId !== "string" || !rawOrgId.trim())) ||
|
|
15
|
+
(hasOrgDomain && (typeof rawOrgDomain !== "string" || !rawOrgDomain.trim()))) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const orgId = typeof rawOrgId === "string" ? rawOrgId.trim() : "";
|
|
19
|
+
const orgDomain = typeof rawOrgDomain === "string" ? rawOrgDomain.trim().toLowerCase() : "";
|
|
20
|
+
if (!orgId && !orgDomain)
|
|
21
|
+
return undefined;
|
|
22
|
+
const context = await import("../org/context.js");
|
|
23
|
+
const organization = orgDomain
|
|
24
|
+
? await context.resolveA2AOrganizationMetadataByDomain(orgDomain)
|
|
25
|
+
: await context.resolveA2AOrganizationMetadataById(orgId);
|
|
26
|
+
if (!organization || (orgId && organization.orgId !== orgId))
|
|
27
|
+
return null;
|
|
28
|
+
const email = typeof payload.sub === "string" ? payload.sub.trim() : "";
|
|
29
|
+
if (!email)
|
|
30
|
+
return null;
|
|
31
|
+
// A shared deployment secret is the cross-app trust grant. The receiver's
|
|
32
|
+
// membership list is not authoritative for a sibling app's user roster.
|
|
33
|
+
return organization;
|
|
34
|
+
}
|
package/dist/a2a/server.d.ts
CHANGED
|
@@ -4,9 +4,8 @@ import type { A2AConfig, AgentSkill } from "./types.js";
|
|
|
4
4
|
/**
|
|
5
5
|
* Result of verifying an inbound A2A JWT. `email` is the caller identity from
|
|
6
6
|
* the token's `sub` claim (null when verification fails), `orgDomain` mirrors
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* cannot resolve that org's domain.
|
|
7
|
+
* locally resolved organization metadata when present, and `orgId` is the
|
|
8
|
+
* matching local organization bound to the token's organization claims.
|
|
10
9
|
*/
|
|
11
10
|
export interface A2ATokenPayload {
|
|
12
11
|
email: string | null;
|
|
@@ -14,15 +13,22 @@ export interface A2ATokenPayload {
|
|
|
14
13
|
orgId?: string;
|
|
15
14
|
claims?: jose.JWTPayload;
|
|
16
15
|
}
|
|
16
|
+
export declare class A2AIdentityVerificationUnavailableError extends Error {
|
|
17
|
+
readonly cause: unknown;
|
|
18
|
+
constructor(cause: unknown);
|
|
19
|
+
}
|
|
20
|
+
export declare function isA2AIdentityVerificationUnavailableError(error: unknown): error is A2AIdentityVerificationUnavailableError;
|
|
17
21
|
/**
|
|
18
22
|
* Verify an inbound A2A bearer token (HS256) exactly as the
|
|
19
23
|
* `/_agent-native/a2a` endpoint does: it peeks at the unverified `org_domain`
|
|
20
|
-
* claim to
|
|
21
|
-
*
|
|
24
|
+
* claim to locate an org credential after trying the global credential, then
|
|
25
|
+
* verifies the JWT — checking
|
|
22
26
|
* `aud`/`iss` when the token carries them and `exp` always. Returns the
|
|
23
27
|
* caller's email (`sub`) and org domain on success, or `{ email: null,
|
|
24
|
-
* orgDomain: null }`
|
|
25
|
-
*
|
|
28
|
+
* orgDomain: null }` for an invalid token or absent credential. If the
|
|
29
|
+
* organization credential or membership evidence cannot be read, throws
|
|
30
|
+
* `A2AIdentityVerificationUnavailableError` so callers cannot treat an
|
|
31
|
+
* unreadable authorization lookup as an unauthenticated fallback.
|
|
26
32
|
*
|
|
27
33
|
* Exported so workspaces can accept A2A callers on the HTTP action route with
|
|
28
34
|
* the same routine — including org-level fallback secrets — instead of
|