@agent-native/core 0.157.20 → 0.157.22
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/templates/analytics/.agents/skills/security/SKILL.md +29 -0
- package/corpus/templates/assets/.agents/skills/security/SKILL.md +29 -0
- package/corpus/templates/brain/.agents/skills/security/SKILL.md +29 -0
- package/corpus/templates/calendar/.agents/skills/security/SKILL.md +29 -0
- package/corpus/templates/chat/.agents/skills/security/SKILL.md +29 -0
- package/corpus/templates/clips/.agents/skills/security/SKILL.md +29 -0
- package/corpus/templates/content/.agents/skills/security/SKILL.md +29 -0
- package/corpus/templates/crm/.agents/skills/security/SKILL.md +29 -0
- package/corpus/templates/design/.agents/skills/security/SKILL.md +29 -0
- package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +29 -0
- package/corpus/templates/factory/.agents/skills/security/SKILL.md +29 -0
- package/corpus/templates/forms/.agents/skills/security/SKILL.md +29 -0
- package/corpus/templates/macros/.agents/skills/security/SKILL.md +29 -0
- package/corpus/templates/mail/.agents/skills/security/SKILL.md +29 -0
- package/corpus/templates/plan/.agents/skills/security/SKILL.md +29 -0
- package/corpus/templates/slides/.agents/skills/security/SKILL.md +29 -0
- package/corpus/templates/tasks/.agents/skills/security/SKILL.md +29 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/embed-route.js +62 -1
- package/dist/server/embed-session.d.ts +11 -0
- package/dist/server/embed-session.js +92 -6
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/templates/chat/.agents/skills/security/SKILL.md +29 -0
- package/dist/templates/default/.agents/skills/security/SKILL.md +29 -0
- package/dist/templates/headless/.agents/skills/security/SKILL.md +29 -0
- package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +29 -0
- package/package.json +1 -1
- package/src/templates/chat/.agents/skills/security/SKILL.md +29 -0
- package/src/templates/default/.agents/skills/security/SKILL.md +29 -0
- package/src/templates/headless/.agents/skills/security/SKILL.md +29 -0
- package/src/templates/workspace-core/.agents/skills/security/SKILL.md +29 -0
|
@@ -41,27 +41,27 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
+
error?: undefined;
|
|
45
|
+
ok?: undefined;
|
|
44
46
|
summary: import("./types.js").TraceSummary;
|
|
45
47
|
spans: import("./types.js").TraceSpan[];
|
|
46
48
|
id?: undefined;
|
|
49
|
+
} | {
|
|
47
50
|
error?: undefined;
|
|
48
51
|
ok?: undefined;
|
|
49
|
-
} | {
|
|
50
52
|
summary?: undefined;
|
|
51
53
|
spans?: undefined;
|
|
52
54
|
id: string;
|
|
53
|
-
error?: undefined;
|
|
54
|
-
ok?: undefined;
|
|
55
55
|
} | {
|
|
56
|
+
ok?: undefined;
|
|
56
57
|
summary?: undefined;
|
|
57
58
|
spans?: undefined;
|
|
58
59
|
id?: undefined;
|
|
59
60
|
error: any;
|
|
60
|
-
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
|
+
error?: undefined;
|
|
62
63
|
summary?: undefined;
|
|
63
64
|
spans?: undefined;
|
|
64
65
|
id?: undefined;
|
|
65
66
|
ok: boolean;
|
|
66
|
-
error?: undefined;
|
|
67
67
|
}>>;
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -34,37 +34,37 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
|
|
|
34
34
|
/** POST /_agent-native/secrets/:key — write a secret. */
|
|
35
35
|
export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
36
36
|
error: string;
|
|
37
|
-
status?: undefined;
|
|
38
37
|
ok?: undefined;
|
|
38
|
+
status?: undefined;
|
|
39
39
|
} | {
|
|
40
|
+
error?: undefined;
|
|
40
41
|
ok: boolean;
|
|
41
42
|
status: string;
|
|
42
|
-
error?: undefined;
|
|
43
43
|
} | {
|
|
44
|
+
ok?: undefined;
|
|
44
45
|
error: string;
|
|
45
46
|
removed?: undefined;
|
|
46
|
-
ok?: undefined;
|
|
47
47
|
} | {
|
|
48
|
+
error?: undefined;
|
|
48
49
|
ok: boolean;
|
|
49
50
|
removed: boolean;
|
|
50
|
-
error?: undefined;
|
|
51
51
|
}>>;
|
|
52
52
|
/**
|
|
53
53
|
* POST /_agent-native/secrets/:key/test — validate an optional candidate value
|
|
54
54
|
* or the current stored value without changing anything.
|
|
55
55
|
*/
|
|
56
56
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
57
|
+
ok?: undefined;
|
|
57
58
|
error: string;
|
|
58
59
|
note?: undefined;
|
|
59
|
-
ok?: undefined;
|
|
60
60
|
} | {
|
|
61
|
+
error?: undefined;
|
|
61
62
|
ok: boolean;
|
|
62
63
|
note?: undefined;
|
|
63
|
-
error?: undefined;
|
|
64
64
|
} | {
|
|
65
|
+
error?: undefined;
|
|
65
66
|
ok: boolean;
|
|
66
67
|
note: string;
|
|
67
|
-
error?: undefined;
|
|
68
68
|
} | {
|
|
69
69
|
note?: undefined;
|
|
70
70
|
ok: boolean;
|
|
@@ -95,11 +95,11 @@ export interface AdHocSecretPayload {
|
|
|
95
95
|
export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
|
|
96
96
|
error: string;
|
|
97
97
|
} | {
|
|
98
|
+
error?: undefined;
|
|
98
99
|
ok: boolean;
|
|
99
100
|
key: string;
|
|
100
|
-
error?: undefined;
|
|
101
101
|
} | {
|
|
102
|
+
error?: undefined;
|
|
102
103
|
ok: boolean;
|
|
103
104
|
removed: boolean;
|
|
104
|
-
error?: undefined;
|
|
105
105
|
}>>;
|
|
@@ -27,10 +27,10 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
|
|
|
27
27
|
export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
28
28
|
error: any;
|
|
29
29
|
} | {
|
|
30
|
+
error?: undefined;
|
|
30
31
|
ok: boolean;
|
|
31
32
|
key: string;
|
|
32
33
|
baseUrlKey?: string;
|
|
33
34
|
scope: AgentEngineApiKeyScope;
|
|
34
|
-
error?: undefined;
|
|
35
35
|
}>>;
|
|
36
36
|
export {};
|
|
@@ -83,6 +83,53 @@ function textResponse(event, message, status) {
|
|
|
83
83
|
}),
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
|
+
function embedTargetOrigin(event) {
|
|
87
|
+
const host = getHeader(event, "host")?.trim();
|
|
88
|
+
if (!host)
|
|
89
|
+
return null;
|
|
90
|
+
const forwardedProto = getHeader(event, "x-forwarded-proto")
|
|
91
|
+
?.split(",", 1)[0]
|
|
92
|
+
?.trim()
|
|
93
|
+
.toLowerCase();
|
|
94
|
+
const protocol = forwardedProto === "http" || forwardedProto === "https"
|
|
95
|
+
? forwardedProto
|
|
96
|
+
: "https";
|
|
97
|
+
return `${protocol}://${host}`;
|
|
98
|
+
}
|
|
99
|
+
function embedTargetAppId(origin) {
|
|
100
|
+
if (origin) {
|
|
101
|
+
try {
|
|
102
|
+
const hostname = new URL(origin).hostname;
|
|
103
|
+
const suffix = ".agent-native.com";
|
|
104
|
+
if (hostname.endsWith(suffix) && hostname.length > suffix.length) {
|
|
105
|
+
return hostname.slice(0, -suffix.length);
|
|
106
|
+
}
|
|
107
|
+
// coercion-ok: diagnostic host parsing must never change the auth response.
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// The host is diagnostic context only; keep the response path independent.
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const configured = [process.env.AGENT_NATIVE_APP_ID, process.env.APP_ID].find((value) => /^[a-z0-9][a-z0-9._-]{0,63}$/i.test(value?.trim() ?? ""));
|
|
114
|
+
if (configured)
|
|
115
|
+
return configured.trim();
|
|
116
|
+
return "unknown";
|
|
117
|
+
}
|
|
118
|
+
function logEmbedConsumeResult(event, diagnostic, responseStatus) {
|
|
119
|
+
const targetOrigin = embedTargetOrigin(event);
|
|
120
|
+
console.info("[agent-native] workspace embed consume", {
|
|
121
|
+
targetAppId: embedTargetAppId(targetOrigin),
|
|
122
|
+
targetOrigin,
|
|
123
|
+
ticketKey: diagnostic.ticketKey,
|
|
124
|
+
outcome: diagnostic.outcome,
|
|
125
|
+
ticketRowFound: diagnostic.ticketRowFound,
|
|
126
|
+
consumed: diagnostic.consumed,
|
|
127
|
+
expired: diagnostic.expired,
|
|
128
|
+
expectedOrgKey: diagnostic.expectedOrgKey,
|
|
129
|
+
ticketOrgKey: diagnostic.ticketOrgKey,
|
|
130
|
+
responseStatus,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
86
133
|
function expiredEmbedSessionResponse(event) {
|
|
87
134
|
setEmbedStartResponseHeaders(event);
|
|
88
135
|
return new Response(`<!doctype html>
|
|
@@ -204,14 +251,24 @@ export function createEmbedStartRouteHandler(options = {}) {
|
|
|
204
251
|
const existingSession = await options
|
|
205
252
|
.getExistingSession?.(event)
|
|
206
253
|
.catch(() => null);
|
|
254
|
+
let consumeDiagnostic = null;
|
|
207
255
|
const consumed = await consumeEmbedSessionTicket(ticket, {
|
|
208
256
|
expectedOrgId: existingSession?.orgId ?? null,
|
|
257
|
+
onResult: (diagnostic) => {
|
|
258
|
+
consumeDiagnostic = diagnostic;
|
|
259
|
+
},
|
|
209
260
|
});
|
|
210
261
|
if (!consumed) {
|
|
262
|
+
if (consumeDiagnostic) {
|
|
263
|
+
logEmbedConsumeResult(event, consumeDiagnostic, 401);
|
|
264
|
+
}
|
|
211
265
|
return expiredEmbedSessionResponse(event);
|
|
212
266
|
}
|
|
213
267
|
const target = normalizeEmbedTargetPath(consumed.targetPath);
|
|
214
268
|
if (!target) {
|
|
269
|
+
if (consumeDiagnostic) {
|
|
270
|
+
logEmbedConsumeResult(event, consumeDiagnostic, 400);
|
|
271
|
+
}
|
|
215
272
|
return textResponse(event, "Invalid embed target.", 400);
|
|
216
273
|
}
|
|
217
274
|
const token = signEmbedSessionToken({
|
|
@@ -230,7 +287,11 @@ export function createEmbedStartRouteHandler(options = {}) {
|
|
|
230
287
|
const chatBridgeActive = firstQueryValue(query[MCP_APP_CHAT_BRIDGE_QUERY_PARAM]) === "1" ||
|
|
231
288
|
firstQueryValue(query[MCP_APP_CHAT_BRIDGE_QUERY_PARAM]) === "true";
|
|
232
289
|
const location = withConfiguredBasePath(withCollapsedAgentSidebarParam(appendEmbedParams(target, token, chatBridgeActive)));
|
|
233
|
-
|
|
290
|
+
const transplant = wantsTransplantLocationResponse(event);
|
|
291
|
+
if (consumeDiagnostic) {
|
|
292
|
+
logEmbedConsumeResult(event, consumeDiagnostic, transplant ? 200 : 302);
|
|
293
|
+
}
|
|
294
|
+
if (transplant) {
|
|
234
295
|
return transplantLocationResponse(event, location);
|
|
235
296
|
}
|
|
236
297
|
return redirectWithStagedCookies(event, location);
|
|
@@ -12,8 +12,19 @@ export interface EmbedSessionTicket {
|
|
|
12
12
|
ticketHash: string;
|
|
13
13
|
expiresAt: number;
|
|
14
14
|
}
|
|
15
|
+
export type EmbedSessionTicketConsumeOutcome = "missing-ticket" | "not-found" | "already-consumed" | "expired" | "org-mismatch" | "consumption-race" | "invalid-row" | "consumed";
|
|
16
|
+
export interface EmbedSessionTicketConsumeDiagnostic {
|
|
17
|
+
outcome: EmbedSessionTicketConsumeOutcome;
|
|
18
|
+
ticketKey: string | null;
|
|
19
|
+
ticketRowFound: boolean;
|
|
20
|
+
consumed: boolean;
|
|
21
|
+
expired: boolean;
|
|
22
|
+
expectedOrgKey: string | null;
|
|
23
|
+
ticketOrgKey: string | null;
|
|
24
|
+
}
|
|
15
25
|
export interface ConsumeEmbedSessionTicketOptions {
|
|
16
26
|
expectedOrgId?: string | null;
|
|
27
|
+
onResult?: (result: EmbedSessionTicketConsumeDiagnostic) => void;
|
|
17
28
|
}
|
|
18
29
|
export interface ConsumedEmbedSessionTicket {
|
|
19
30
|
ownerEmail: string;
|
|
@@ -133,6 +133,11 @@ function signPayload(payload) {
|
|
|
133
133
|
function hashTicket(ticket) {
|
|
134
134
|
return crypto.createHash("sha256").update(ticket).digest("hex");
|
|
135
135
|
}
|
|
136
|
+
function redactedIdentifier(value) {
|
|
137
|
+
if (!value)
|
|
138
|
+
return null;
|
|
139
|
+
return crypto.createHash("sha256").update(value).digest("hex").slice(0, 12);
|
|
140
|
+
}
|
|
136
141
|
function numberOrNull(value) {
|
|
137
142
|
if (value == null)
|
|
138
143
|
return null;
|
|
@@ -458,27 +463,79 @@ export async function createEmbedSessionTicket(input) {
|
|
|
458
463
|
return { ticket, ticketHash, expiresAt };
|
|
459
464
|
}
|
|
460
465
|
export async function consumeEmbedSessionTicket(ticket, options = {}) {
|
|
461
|
-
|
|
466
|
+
const expectedOrgKey = redactedIdentifier(options.expectedOrgId);
|
|
467
|
+
if (!ticket) {
|
|
468
|
+
options.onResult?.({
|
|
469
|
+
outcome: "missing-ticket",
|
|
470
|
+
ticketKey: null,
|
|
471
|
+
ticketRowFound: false,
|
|
472
|
+
consumed: false,
|
|
473
|
+
expired: false,
|
|
474
|
+
expectedOrgKey,
|
|
475
|
+
ticketOrgKey: null,
|
|
476
|
+
});
|
|
462
477
|
return null;
|
|
478
|
+
}
|
|
463
479
|
await ensureTable();
|
|
464
480
|
const ticketHash = hashTicket(ticket);
|
|
481
|
+
const ticketKey = ticketHash.slice(0, 12);
|
|
465
482
|
const now = Date.now();
|
|
466
483
|
const { rows } = await getDbExec().execute({
|
|
467
484
|
sql: "SELECT ticket_hash, owner_email, org_id, target_path, scope, expires_at, consumed_at " +
|
|
468
485
|
"FROM agent_native_embed_tickets WHERE ticket_hash = ?",
|
|
469
486
|
args: [ticketHash],
|
|
470
487
|
});
|
|
471
|
-
if (rows.length === 0)
|
|
488
|
+
if (rows.length === 0) {
|
|
489
|
+
options.onResult?.({
|
|
490
|
+
outcome: "not-found",
|
|
491
|
+
ticketKey,
|
|
492
|
+
ticketRowFound: false,
|
|
493
|
+
consumed: false,
|
|
494
|
+
expired: false,
|
|
495
|
+
expectedOrgKey,
|
|
496
|
+
ticketOrgKey: null,
|
|
497
|
+
});
|
|
472
498
|
return null;
|
|
499
|
+
}
|
|
473
500
|
const row = rows[0];
|
|
474
501
|
const expiresAt = numberOrNull(row.expires_at ?? row.expiresAt);
|
|
475
502
|
const consumedAt = numberOrNull(row.consumed_at ?? row.consumedAt);
|
|
476
503
|
const orgId = stringOrUndefined(row.org_id ?? row.orgId);
|
|
477
|
-
|
|
504
|
+
const ticketOrgKey = redactedIdentifier(orgId);
|
|
505
|
+
if (consumedAt != null) {
|
|
506
|
+
options.onResult?.({
|
|
507
|
+
outcome: "already-consumed",
|
|
508
|
+
ticketKey,
|
|
509
|
+
ticketRowFound: true,
|
|
510
|
+
consumed: true,
|
|
511
|
+
expired: false,
|
|
512
|
+
expectedOrgKey,
|
|
513
|
+
ticketOrgKey,
|
|
514
|
+
});
|
|
478
515
|
return null;
|
|
479
|
-
|
|
516
|
+
}
|
|
517
|
+
if (expiresAt != null && expiresAt < now) {
|
|
518
|
+
options.onResult?.({
|
|
519
|
+
outcome: "expired",
|
|
520
|
+
ticketKey,
|
|
521
|
+
ticketRowFound: true,
|
|
522
|
+
consumed: false,
|
|
523
|
+
expired: true,
|
|
524
|
+
expectedOrgKey,
|
|
525
|
+
ticketOrgKey,
|
|
526
|
+
});
|
|
480
527
|
return null;
|
|
528
|
+
}
|
|
481
529
|
if (options.expectedOrgId && orgId && orgId !== options.expectedOrgId) {
|
|
530
|
+
options.onResult?.({
|
|
531
|
+
outcome: "org-mismatch",
|
|
532
|
+
ticketKey,
|
|
533
|
+
ticketRowFound: true,
|
|
534
|
+
consumed: false,
|
|
535
|
+
expired: false,
|
|
536
|
+
expectedOrgKey,
|
|
537
|
+
ticketOrgKey,
|
|
538
|
+
});
|
|
482
539
|
return null;
|
|
483
540
|
}
|
|
484
541
|
const result = await getDbExec().execute({
|
|
@@ -486,12 +543,41 @@ export async function consumeEmbedSessionTicket(ticket, options = {}) {
|
|
|
486
543
|
"WHERE ticket_hash = ? AND consumed_at IS NULL",
|
|
487
544
|
args: [now, ticketHash],
|
|
488
545
|
});
|
|
489
|
-
if (result.rowsAffected === 0)
|
|
546
|
+
if (result.rowsAffected === 0) {
|
|
547
|
+
options.onResult?.({
|
|
548
|
+
outcome: "consumption-race",
|
|
549
|
+
ticketKey,
|
|
550
|
+
ticketRowFound: true,
|
|
551
|
+
consumed: false,
|
|
552
|
+
expired: false,
|
|
553
|
+
expectedOrgKey,
|
|
554
|
+
ticketOrgKey,
|
|
555
|
+
});
|
|
490
556
|
return null;
|
|
557
|
+
}
|
|
491
558
|
const targetPath = normalizeEmbedTargetPath(stringOrUndefined(row.target_path ?? row.targetPath));
|
|
492
559
|
const ownerEmail = stringOrUndefined(row.owner_email ?? row.ownerEmail);
|
|
493
|
-
if (!targetPath || !ownerEmail || expiresAt == null)
|
|
560
|
+
if (!targetPath || !ownerEmail || expiresAt == null) {
|
|
561
|
+
options.onResult?.({
|
|
562
|
+
outcome: "invalid-row",
|
|
563
|
+
ticketKey,
|
|
564
|
+
ticketRowFound: true,
|
|
565
|
+
consumed: true,
|
|
566
|
+
expired: false,
|
|
567
|
+
expectedOrgKey,
|
|
568
|
+
ticketOrgKey,
|
|
569
|
+
});
|
|
494
570
|
return null;
|
|
571
|
+
}
|
|
572
|
+
options.onResult?.({
|
|
573
|
+
outcome: "consumed",
|
|
574
|
+
ticketKey,
|
|
575
|
+
ticketRowFound: true,
|
|
576
|
+
consumed: true,
|
|
577
|
+
expired: false,
|
|
578
|
+
expectedOrgKey,
|
|
579
|
+
ticketOrgKey,
|
|
580
|
+
});
|
|
495
581
|
return {
|
|
496
582
|
ownerEmail,
|
|
497
583
|
...(orgId ? { orgId } : {}),
|
|
@@ -26,8 +26,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
|
|
|
26
26
|
expiresAt?: undefined;
|
|
27
27
|
ttlSeconds?: undefined;
|
|
28
28
|
} | {
|
|
29
|
-
error?: undefined;
|
|
30
29
|
token: string;
|
|
31
30
|
expiresAt: string;
|
|
32
31
|
ttlSeconds: number;
|
|
32
|
+
error?: undefined;
|
|
33
33
|
}>>;
|
|
@@ -149,6 +149,35 @@ export default defineEventHandler(async (event) => {
|
|
|
149
149
|
|
|
150
150
|
- Never create unprotected routes that modify data.
|
|
151
151
|
|
|
152
|
+
## Same-Origin Workspace Apps
|
|
153
|
+
|
|
154
|
+
Path-mounted workspace apps share the Dispatch gateway origin. Because the
|
|
155
|
+
framework's session cookie is scoped to `/`, a mounted pane receives the
|
|
156
|
+
ambient Dispatch session and can act as the signed-in user through same-origin
|
|
157
|
+
requests. This is an intentional trusted-code model, not an isolation
|
|
158
|
+
boundary; removing a mounted app from SSO fanout does not revoke that ambient
|
|
159
|
+
access.
|
|
160
|
+
|
|
161
|
+
Only trusted, workspace-owner-authored code belongs on that origin. Treat each
|
|
162
|
+
of these as a trust-boundary change that requires an explicit origin, sandbox,
|
|
163
|
+
or capability design before shipping:
|
|
164
|
+
|
|
165
|
+
- non-owners can create or edit mounted app code;
|
|
166
|
+
- mounted apps render or execute untrusted external content or remote code;
|
|
167
|
+
- apps are publicly shareable, anonymously reachable, or installable by users
|
|
168
|
+
outside the owning workspace;
|
|
169
|
+
- app source, dependencies, or deployment artifacts can be replaced by a
|
|
170
|
+
third party without the workspace owner's authorization;
|
|
171
|
+
- a mounted app can be registered across workspaces or can change its mount
|
|
172
|
+
path/origin without an ownership check; or
|
|
173
|
+
- cookie scope, proxying, iframe policy, or navigation changes make this
|
|
174
|
+
ambient-session behavior broader than the owning workspace.
|
|
175
|
+
|
|
176
|
+
Do not describe canonical-only SSO eligibility as origin isolation. Canonical
|
|
177
|
+
apps and explicitly registered custom origins remain eligible; path-mounted
|
|
178
|
+
apps are deliberately excluded as SSO targets while retaining their existing
|
|
179
|
+
same-origin session behavior.
|
|
180
|
+
|
|
152
181
|
**Exception — the SSR HTML/`.data` catch-all is deliberately session-blind.**
|
|
153
182
|
The rule above is for routes that read or mutate user data. The SSR page
|
|
154
183
|
render and React Router `.data` route are different: they serve one
|
|
@@ -149,6 +149,35 @@ export default defineEventHandler(async (event) => {
|
|
|
149
149
|
|
|
150
150
|
- Never create unprotected routes that modify data.
|
|
151
151
|
|
|
152
|
+
## Same-Origin Workspace Apps
|
|
153
|
+
|
|
154
|
+
Path-mounted workspace apps share the Dispatch gateway origin. Because the
|
|
155
|
+
framework's session cookie is scoped to `/`, a mounted pane receives the
|
|
156
|
+
ambient Dispatch session and can act as the signed-in user through same-origin
|
|
157
|
+
requests. This is an intentional trusted-code model, not an isolation
|
|
158
|
+
boundary; removing a mounted app from SSO fanout does not revoke that ambient
|
|
159
|
+
access.
|
|
160
|
+
|
|
161
|
+
Only trusted, workspace-owner-authored code belongs on that origin. Treat each
|
|
162
|
+
of these as a trust-boundary change that requires an explicit origin, sandbox,
|
|
163
|
+
or capability design before shipping:
|
|
164
|
+
|
|
165
|
+
- non-owners can create or edit mounted app code;
|
|
166
|
+
- mounted apps render or execute untrusted external content or remote code;
|
|
167
|
+
- apps are publicly shareable, anonymously reachable, or installable by users
|
|
168
|
+
outside the owning workspace;
|
|
169
|
+
- app source, dependencies, or deployment artifacts can be replaced by a
|
|
170
|
+
third party without the workspace owner's authorization;
|
|
171
|
+
- a mounted app can be registered across workspaces or can change its mount
|
|
172
|
+
path/origin without an ownership check; or
|
|
173
|
+
- cookie scope, proxying, iframe policy, or navigation changes make this
|
|
174
|
+
ambient-session behavior broader than the owning workspace.
|
|
175
|
+
|
|
176
|
+
Do not describe canonical-only SSO eligibility as origin isolation. Canonical
|
|
177
|
+
apps and explicitly registered custom origins remain eligible; path-mounted
|
|
178
|
+
apps are deliberately excluded as SSO targets while retaining their existing
|
|
179
|
+
same-origin session behavior.
|
|
180
|
+
|
|
152
181
|
**Exception — the SSR HTML/`.data` catch-all is deliberately session-blind.**
|
|
153
182
|
The rule above is for routes that read or mutate user data. The SSR page
|
|
154
183
|
render and React Router `.data` route are different: they serve one
|
|
@@ -149,6 +149,35 @@ export default defineEventHandler(async (event) => {
|
|
|
149
149
|
|
|
150
150
|
- Never create unprotected routes that modify data.
|
|
151
151
|
|
|
152
|
+
## Same-Origin Workspace Apps
|
|
153
|
+
|
|
154
|
+
Path-mounted workspace apps share the Dispatch gateway origin. Because the
|
|
155
|
+
framework's session cookie is scoped to `/`, a mounted pane receives the
|
|
156
|
+
ambient Dispatch session and can act as the signed-in user through same-origin
|
|
157
|
+
requests. This is an intentional trusted-code model, not an isolation
|
|
158
|
+
boundary; removing a mounted app from SSO fanout does not revoke that ambient
|
|
159
|
+
access.
|
|
160
|
+
|
|
161
|
+
Only trusted, workspace-owner-authored code belongs on that origin. Treat each
|
|
162
|
+
of these as a trust-boundary change that requires an explicit origin, sandbox,
|
|
163
|
+
or capability design before shipping:
|
|
164
|
+
|
|
165
|
+
- non-owners can create or edit mounted app code;
|
|
166
|
+
- mounted apps render or execute untrusted external content or remote code;
|
|
167
|
+
- apps are publicly shareable, anonymously reachable, or installable by users
|
|
168
|
+
outside the owning workspace;
|
|
169
|
+
- app source, dependencies, or deployment artifacts can be replaced by a
|
|
170
|
+
third party without the workspace owner's authorization;
|
|
171
|
+
- a mounted app can be registered across workspaces or can change its mount
|
|
172
|
+
path/origin without an ownership check; or
|
|
173
|
+
- cookie scope, proxying, iframe policy, or navigation changes make this
|
|
174
|
+
ambient-session behavior broader than the owning workspace.
|
|
175
|
+
|
|
176
|
+
Do not describe canonical-only SSO eligibility as origin isolation. Canonical
|
|
177
|
+
apps and explicitly registered custom origins remain eligible; path-mounted
|
|
178
|
+
apps are deliberately excluded as SSO targets while retaining their existing
|
|
179
|
+
same-origin session behavior.
|
|
180
|
+
|
|
152
181
|
**Exception — the SSR HTML/`.data` catch-all is deliberately session-blind.**
|
|
153
182
|
The rule above is for routes that read or mutate user data. The SSR page
|
|
154
183
|
render and React Router `.data` route are different: they serve one
|
|
@@ -149,6 +149,35 @@ export default defineEventHandler(async (event) => {
|
|
|
149
149
|
|
|
150
150
|
- Never create unprotected routes that modify data.
|
|
151
151
|
|
|
152
|
+
## Same-Origin Workspace Apps
|
|
153
|
+
|
|
154
|
+
Path-mounted workspace apps share the Dispatch gateway origin. Because the
|
|
155
|
+
framework's session cookie is scoped to `/`, a mounted pane receives the
|
|
156
|
+
ambient Dispatch session and can act as the signed-in user through same-origin
|
|
157
|
+
requests. This is an intentional trusted-code model, not an isolation
|
|
158
|
+
boundary; removing a mounted app from SSO fanout does not revoke that ambient
|
|
159
|
+
access.
|
|
160
|
+
|
|
161
|
+
Only trusted, workspace-owner-authored code belongs on that origin. Treat each
|
|
162
|
+
of these as a trust-boundary change that requires an explicit origin, sandbox,
|
|
163
|
+
or capability design before shipping:
|
|
164
|
+
|
|
165
|
+
- non-owners can create or edit mounted app code;
|
|
166
|
+
- mounted apps render or execute untrusted external content or remote code;
|
|
167
|
+
- apps are publicly shareable, anonymously reachable, or installable by users
|
|
168
|
+
outside the owning workspace;
|
|
169
|
+
- app source, dependencies, or deployment artifacts can be replaced by a
|
|
170
|
+
third party without the workspace owner's authorization;
|
|
171
|
+
- a mounted app can be registered across workspaces or can change its mount
|
|
172
|
+
path/origin without an ownership check; or
|
|
173
|
+
- cookie scope, proxying, iframe policy, or navigation changes make this
|
|
174
|
+
ambient-session behavior broader than the owning workspace.
|
|
175
|
+
|
|
176
|
+
Do not describe canonical-only SSO eligibility as origin isolation. Canonical
|
|
177
|
+
apps and explicitly registered custom origins remain eligible; path-mounted
|
|
178
|
+
apps are deliberately excluded as SSO targets while retaining their existing
|
|
179
|
+
same-origin session behavior.
|
|
180
|
+
|
|
152
181
|
**Exception — the SSR HTML/`.data` catch-all is deliberately session-blind.**
|
|
153
182
|
The rule above is for routes that read or mutate user data. The SSR page
|
|
154
183
|
render and React Router `.data` route are different: they serve one
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.157.
|
|
3
|
+
"version": "0.157.22",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -149,6 +149,35 @@ export default defineEventHandler(async (event) => {
|
|
|
149
149
|
|
|
150
150
|
- Never create unprotected routes that modify data.
|
|
151
151
|
|
|
152
|
+
## Same-Origin Workspace Apps
|
|
153
|
+
|
|
154
|
+
Path-mounted workspace apps share the Dispatch gateway origin. Because the
|
|
155
|
+
framework's session cookie is scoped to `/`, a mounted pane receives the
|
|
156
|
+
ambient Dispatch session and can act as the signed-in user through same-origin
|
|
157
|
+
requests. This is an intentional trusted-code model, not an isolation
|
|
158
|
+
boundary; removing a mounted app from SSO fanout does not revoke that ambient
|
|
159
|
+
access.
|
|
160
|
+
|
|
161
|
+
Only trusted, workspace-owner-authored code belongs on that origin. Treat each
|
|
162
|
+
of these as a trust-boundary change that requires an explicit origin, sandbox,
|
|
163
|
+
or capability design before shipping:
|
|
164
|
+
|
|
165
|
+
- non-owners can create or edit mounted app code;
|
|
166
|
+
- mounted apps render or execute untrusted external content or remote code;
|
|
167
|
+
- apps are publicly shareable, anonymously reachable, or installable by users
|
|
168
|
+
outside the owning workspace;
|
|
169
|
+
- app source, dependencies, or deployment artifacts can be replaced by a
|
|
170
|
+
third party without the workspace owner's authorization;
|
|
171
|
+
- a mounted app can be registered across workspaces or can change its mount
|
|
172
|
+
path/origin without an ownership check; or
|
|
173
|
+
- cookie scope, proxying, iframe policy, or navigation changes make this
|
|
174
|
+
ambient-session behavior broader than the owning workspace.
|
|
175
|
+
|
|
176
|
+
Do not describe canonical-only SSO eligibility as origin isolation. Canonical
|
|
177
|
+
apps and explicitly registered custom origins remain eligible; path-mounted
|
|
178
|
+
apps are deliberately excluded as SSO targets while retaining their existing
|
|
179
|
+
same-origin session behavior.
|
|
180
|
+
|
|
152
181
|
**Exception — the SSR HTML/`.data` catch-all is deliberately session-blind.**
|
|
153
182
|
The rule above is for routes that read or mutate user data. The SSR page
|
|
154
183
|
render and React Router `.data` route are different: they serve one
|
|
@@ -149,6 +149,35 @@ export default defineEventHandler(async (event) => {
|
|
|
149
149
|
|
|
150
150
|
- Never create unprotected routes that modify data.
|
|
151
151
|
|
|
152
|
+
## Same-Origin Workspace Apps
|
|
153
|
+
|
|
154
|
+
Path-mounted workspace apps share the Dispatch gateway origin. Because the
|
|
155
|
+
framework's session cookie is scoped to `/`, a mounted pane receives the
|
|
156
|
+
ambient Dispatch session and can act as the signed-in user through same-origin
|
|
157
|
+
requests. This is an intentional trusted-code model, not an isolation
|
|
158
|
+
boundary; removing a mounted app from SSO fanout does not revoke that ambient
|
|
159
|
+
access.
|
|
160
|
+
|
|
161
|
+
Only trusted, workspace-owner-authored code belongs on that origin. Treat each
|
|
162
|
+
of these as a trust-boundary change that requires an explicit origin, sandbox,
|
|
163
|
+
or capability design before shipping:
|
|
164
|
+
|
|
165
|
+
- non-owners can create or edit mounted app code;
|
|
166
|
+
- mounted apps render or execute untrusted external content or remote code;
|
|
167
|
+
- apps are publicly shareable, anonymously reachable, or installable by users
|
|
168
|
+
outside the owning workspace;
|
|
169
|
+
- app source, dependencies, or deployment artifacts can be replaced by a
|
|
170
|
+
third party without the workspace owner's authorization;
|
|
171
|
+
- a mounted app can be registered across workspaces or can change its mount
|
|
172
|
+
path/origin without an ownership check; or
|
|
173
|
+
- cookie scope, proxying, iframe policy, or navigation changes make this
|
|
174
|
+
ambient-session behavior broader than the owning workspace.
|
|
175
|
+
|
|
176
|
+
Do not describe canonical-only SSO eligibility as origin isolation. Canonical
|
|
177
|
+
apps and explicitly registered custom origins remain eligible; path-mounted
|
|
178
|
+
apps are deliberately excluded as SSO targets while retaining their existing
|
|
179
|
+
same-origin session behavior.
|
|
180
|
+
|
|
152
181
|
**Exception — the SSR HTML/`.data` catch-all is deliberately session-blind.**
|
|
153
182
|
The rule above is for routes that read or mutate user data. The SSR page
|
|
154
183
|
render and React Router `.data` route are different: they serve one
|
|
@@ -149,6 +149,35 @@ export default defineEventHandler(async (event) => {
|
|
|
149
149
|
|
|
150
150
|
- Never create unprotected routes that modify data.
|
|
151
151
|
|
|
152
|
+
## Same-Origin Workspace Apps
|
|
153
|
+
|
|
154
|
+
Path-mounted workspace apps share the Dispatch gateway origin. Because the
|
|
155
|
+
framework's session cookie is scoped to `/`, a mounted pane receives the
|
|
156
|
+
ambient Dispatch session and can act as the signed-in user through same-origin
|
|
157
|
+
requests. This is an intentional trusted-code model, not an isolation
|
|
158
|
+
boundary; removing a mounted app from SSO fanout does not revoke that ambient
|
|
159
|
+
access.
|
|
160
|
+
|
|
161
|
+
Only trusted, workspace-owner-authored code belongs on that origin. Treat each
|
|
162
|
+
of these as a trust-boundary change that requires an explicit origin, sandbox,
|
|
163
|
+
or capability design before shipping:
|
|
164
|
+
|
|
165
|
+
- non-owners can create or edit mounted app code;
|
|
166
|
+
- mounted apps render or execute untrusted external content or remote code;
|
|
167
|
+
- apps are publicly shareable, anonymously reachable, or installable by users
|
|
168
|
+
outside the owning workspace;
|
|
169
|
+
- app source, dependencies, or deployment artifacts can be replaced by a
|
|
170
|
+
third party without the workspace owner's authorization;
|
|
171
|
+
- a mounted app can be registered across workspaces or can change its mount
|
|
172
|
+
path/origin without an ownership check; or
|
|
173
|
+
- cookie scope, proxying, iframe policy, or navigation changes make this
|
|
174
|
+
ambient-session behavior broader than the owning workspace.
|
|
175
|
+
|
|
176
|
+
Do not describe canonical-only SSO eligibility as origin isolation. Canonical
|
|
177
|
+
apps and explicitly registered custom origins remain eligible; path-mounted
|
|
178
|
+
apps are deliberately excluded as SSO targets while retaining their existing
|
|
179
|
+
same-origin session behavior.
|
|
180
|
+
|
|
152
181
|
**Exception — the SSR HTML/`.data` catch-all is deliberately session-blind.**
|
|
153
182
|
The rule above is for routes that read or mutate user data. The SSR page
|
|
154
183
|
render and React Router `.data` route are different: they serve one
|