@m6d/cortex-angular 1.3.0 → 1.4.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/README.md +18 -11
- package/fesm2022/m6d-cortex-angular.mjs +23 -5
- package/package.json +1 -1
- package/types/m6d-cortex-angular.d.ts +10 -0
package/README.md
CHANGED
|
@@ -27,16 +27,17 @@ No root-level providers needed — each widget instance is fully self-contained.
|
|
|
27
27
|
|
|
28
28
|
### Configuration
|
|
29
29
|
|
|
30
|
-
| Property
|
|
31
|
-
|
|
|
32
|
-
| `locale`
|
|
33
|
-
| `transport.baseUrl`
|
|
34
|
-
| `transport.getHeaders`
|
|
35
|
-
| `wsUrl`
|
|
36
|
-
| `theme`
|
|
37
|
-
| `showDebugButton`
|
|
38
|
-
| `toolComponents`
|
|
39
|
-
| `onToolCall`
|
|
30
|
+
| Property | Type | Description |
|
|
31
|
+
| ----------------------- | ------------------------------- | ------------------------------------------------------------------------------- |
|
|
32
|
+
| `locale` | `Signal<string>` | Active locale (`"en"`, `"ar"`) |
|
|
33
|
+
| `transport.baseUrl` | `string \| Signal<string>` | Agent API base URL |
|
|
34
|
+
| `transport.getHeaders` | `() => Record<string, string>` | Request headers provider |
|
|
35
|
+
| `wsUrl` | `string` | WebSocket URL for real-time events (optional) |
|
|
36
|
+
| `theme` | `CortexClientTheme` | `'light' \| 'dark' \| 'system'` (default: `'light'`) |
|
|
37
|
+
| `showDebugButton` | `boolean` | Show/hide the debug toggle button (default: `false`) |
|
|
38
|
+
| `toolComponents` | `Record<string, Type<unknown>>` | Custom Angular components for tool rendering (optional) |
|
|
39
|
+
| `onToolCall` | `(toolCall) => unknown` | Client-side tool call handler (optional) |
|
|
40
|
+
| `trustSameOriginEmbeds` | `boolean` | Allow embedded tool pages served from this page's own origin (default: `false`) |
|
|
40
41
|
|
|
41
42
|
`transport.getHeaders` can return any request headers needed by the host app. WebSocket auth continues to derive the `token` query param from a bearer `Authorization` header when present.
|
|
42
43
|
|
|
@@ -48,7 +49,13 @@ initiate, shows the returned page in a sandboxed iframe (inline card or
|
|
|
48
49
|
modal, per tool config), and submits the page's `cortex:client-tool`
|
|
49
50
|
postMessage outcome back to the run as-is — anything that must actually be
|
|
50
51
|
true lives in the integrating backend's own records. No widget configuration
|
|
51
|
-
is needed
|
|
52
|
+
is needed. One caveat: an embed served from the host page's **own origin** is
|
|
53
|
+
refused by default, because a same-origin iframe cannot be sandboxed — it
|
|
54
|
+
could reach into the host page, and embed URLs come from Control Center tool
|
|
55
|
+
config, whose administrator is not necessarily the site owner. When both are
|
|
56
|
+
the same party and the embeds are the site's own pages, set
|
|
57
|
+
`trustSameOriginEmbeds: true` to render them anyway (with full access, as any
|
|
58
|
+
of the site's own pages has). Other pending tool calls keep the existing `toolComponents` /
|
|
52
59
|
`hooks.onToolCall` behavior. That hook is also how static client tools work:
|
|
53
60
|
declare an executor-less tool in the server config and answer it here by
|
|
54
61
|
returning its output from `hooks.onToolCall` for that tool name. Consumer `hooks.onToolCall` implementations must
|
|
@@ -483,14 +483,22 @@ function parseClientToolHandshake(data, origin, embedOrigin) {
|
|
|
483
483
|
}
|
|
484
484
|
|
|
485
485
|
// src/internal/client/src/client-tools.ts
|
|
486
|
-
function embedSandbox(embedUrl, pageOrigin) {
|
|
486
|
+
function embedSandbox(embedUrl, pageOrigin, trustSameOrigin) {
|
|
487
487
|
const base = "allow-scripts allow-forms allow-popups";
|
|
488
488
|
try {
|
|
489
|
-
|
|
489
|
+
const sameOrigin = new URL(embedUrl).origin === pageOrigin;
|
|
490
|
+
return sameOrigin && !trustSameOrigin ? base : `${base} allow-same-origin`;
|
|
490
491
|
} catch {
|
|
491
492
|
return base;
|
|
492
493
|
}
|
|
493
494
|
}
|
|
495
|
+
function embedTrustedOnHostOrigin(embedUrl, pageOrigin, trustSameOrigin) {
|
|
496
|
+
try {
|
|
497
|
+
return trustSameOrigin === true && new URL(embedUrl).origin === pageOrigin;
|
|
498
|
+
} catch {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
494
502
|
function frameBreachedHostOrigin(frame, pageOrigin) {
|
|
495
503
|
try {
|
|
496
504
|
return frame.contentWindow?.location.origin === pageOrigin;
|
|
@@ -1468,9 +1476,11 @@ class CortexChatService {
|
|
|
1468
1476
|
return this.threadService.delete(threadId);
|
|
1469
1477
|
}
|
|
1470
1478
|
mountChat(thread, mode) {
|
|
1479
|
+
const initialMessages = mode === "reload" ? this.chat()?.messages() : undefined;
|
|
1471
1480
|
this.chatInjector?.destroy();
|
|
1472
1481
|
this.dispatchedToolCalls.clear();
|
|
1473
|
-
|
|
1482
|
+
if (mode !== "reload")
|
|
1483
|
+
this.messageMetadata.set(new Map);
|
|
1474
1484
|
this.ownsRun = false;
|
|
1475
1485
|
this.serverAnswered = false;
|
|
1476
1486
|
this.wasAborted = false;
|
|
@@ -1481,6 +1491,7 @@ class CortexChatService {
|
|
|
1481
1491
|
const chat = injectChat({
|
|
1482
1492
|
threadId: thread.id,
|
|
1483
1493
|
persistence: true,
|
|
1494
|
+
initialMessages,
|
|
1484
1495
|
connection: this.createConnection(thread, mode, injector),
|
|
1485
1496
|
onChunk: () => {
|
|
1486
1497
|
this.serverAnswered = true;
|
|
@@ -3464,8 +3475,10 @@ class ClientToolEmbedComponent {
|
|
|
3464
3475
|
}, ...ngDevMode ? [{ debugName: "isModal" }] : []);
|
|
3465
3476
|
sandbox = computed(() => {
|
|
3466
3477
|
const state = this.state();
|
|
3467
|
-
return state.phase === "embedded" ? embedSandbox(state.embedUrl, window.location.origin) : "";
|
|
3478
|
+
return state.phase === "embedded" ? embedSandbox(state.embedUrl, window.location.origin, this.trustSameOriginEmbeds) : "";
|
|
3468
3479
|
}, ...ngDevMode ? [{ debugName: "sandbox" }] : []);
|
|
3480
|
+
config = inject(CORTEX_CLIENT_CONFIG);
|
|
3481
|
+
trustSameOriginEmbeds;
|
|
3469
3482
|
api = inject(CortexApiClient);
|
|
3470
3483
|
chatService = inject(CortexChatService);
|
|
3471
3484
|
sanitizer = inject(DomSanitizer);
|
|
@@ -3486,6 +3499,7 @@ class ClientToolEmbedComponent {
|
|
|
3486
3499
|
});
|
|
3487
3500
|
}
|
|
3488
3501
|
ngOnInit() {
|
|
3502
|
+
this.trustSameOriginEmbeds = this.config().trustSameOriginEmbeds;
|
|
3489
3503
|
const threadId = this.chatService.selectedThread()?.id;
|
|
3490
3504
|
const part = this.toolCallPart();
|
|
3491
3505
|
if (!threadId) {
|
|
@@ -3516,7 +3530,11 @@ class ClientToolEmbedComponent {
|
|
|
3516
3530
|
const frame = event.target;
|
|
3517
3531
|
if (!(frame instanceof HTMLIFrameElement))
|
|
3518
3532
|
return;
|
|
3519
|
-
|
|
3533
|
+
const state = this.state();
|
|
3534
|
+
if (state.phase !== "embedded")
|
|
3535
|
+
return;
|
|
3536
|
+
const trusted = embedTrustedOnHostOrigin(state.embedUrl, window.location.origin, this.trustSameOriginEmbeds);
|
|
3537
|
+
if (!trusted && frameBreachedHostOrigin(frame, window.location.origin)) {
|
|
3520
3538
|
this.embed?.terminate();
|
|
3521
3539
|
}
|
|
3522
3540
|
}
|
package/package.json
CHANGED
|
@@ -106,6 +106,16 @@ export type CortexConfig = {
|
|
|
106
106
|
/** Color scheme; `'system'` follows the OS preference. Defaults to `'light'`. */
|
|
107
107
|
theme?: CortexTheme;
|
|
108
108
|
showDebugButton?: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Render embedded tool pages served from this page's own origin with their
|
|
111
|
+
* full identity — which, same-origin, means no effective sandbox at all: such
|
|
112
|
+
* an embed can reach into the host page. By default they are refused
|
|
113
|
+
* (rendered inert), because embed URLs come from Control Center tool config,
|
|
114
|
+
* and whoever administers that is not necessarily whoever owns this site.
|
|
115
|
+
* Enable only when both are the same party and the embeds are this site's
|
|
116
|
+
* own pages — then a second origin just for embeds is no longer needed.
|
|
117
|
+
*/
|
|
118
|
+
trustSameOriginEmbeds?: boolean;
|
|
109
119
|
hooks?: CortexHooks;
|
|
110
120
|
};
|
|
111
121
|
export type QueuedAttachment = {
|