@meshmakers/octo-ai-console 3.4.150 → 3.4.170
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.
|
@@ -161,10 +161,10 @@ class AiAdapterClientService {
|
|
|
161
161
|
tenantBase() {
|
|
162
162
|
return `${this.options.baseUrl}/${encodeURIComponent(this.options.tenantId)}/v1`;
|
|
163
163
|
}
|
|
164
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
165
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
164
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiAdapterClientService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
165
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiAdapterClientService });
|
|
166
166
|
}
|
|
167
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
167
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiAdapterClientService, decorators: [{
|
|
168
168
|
type: Injectable
|
|
169
169
|
}] });
|
|
170
170
|
|
|
@@ -308,10 +308,10 @@ class AiSessionStreamService {
|
|
|
308
308
|
: `/${this.options.hubPath}`;
|
|
309
309
|
return `${this.options.baseUrl}/${this.options.tenantId}${trimmed}`;
|
|
310
310
|
}
|
|
311
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
312
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
311
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiSessionStreamService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
312
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiSessionStreamService });
|
|
313
313
|
}
|
|
314
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
314
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiSessionStreamService, decorators: [{
|
|
315
315
|
type: Injectable
|
|
316
316
|
}] });
|
|
317
317
|
|
|
@@ -383,10 +383,10 @@ class AiJobStatusBadgeComponent {
|
|
|
383
383
|
return 'queued';
|
|
384
384
|
}
|
|
385
385
|
}, ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
386
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
387
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.
|
|
386
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiJobStatusBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
387
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.17", type: AiJobStatusBadgeComponent, isStandalone: true, selector: "mm-ai-job-status-badge", inputs: { status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<span class=\"mm-ai-status-badge mm-ai-status-badge--{{ variant() }}\">\n {{ status() }}\n</span>\n", styles: [":host{display:inline-block;--mm-ai-status-running-bg: #1f6feb;--mm-ai-status-running-fg: #ffffff;--mm-ai-status-queued-bg: #6e7681;--mm-ai-status-queued-fg: #ffffff;--mm-ai-status-paused-bg: #d29922;--mm-ai-status-paused-fg: #1c1c1c;--mm-ai-status-completed-bg: #2ea44f;--mm-ai-status-completed-fg: #ffffff;--mm-ai-status-failed-bg: #d73a49;--mm-ai-status-failed-fg: #ffffff;--mm-ai-status-blocked-bg: #8957e5;--mm-ai-status-blocked-fg: #ffffff}.mm-ai-status-badge{display:inline-block;padding:.15rem .65rem;border-radius:9999px;font-size:.8rem;font-weight:600;line-height:1.2;letter-spacing:.01em}.mm-ai-status-badge--running{background:var(--mm-ai-status-running-bg);color:var(--mm-ai-status-running-fg)}.mm-ai-status-badge--queued{background:var(--mm-ai-status-queued-bg);color:var(--mm-ai-status-queued-fg)}.mm-ai-status-badge--paused{background:var(--mm-ai-status-paused-bg);color:var(--mm-ai-status-paused-fg)}.mm-ai-status-badge--completed{background:var(--mm-ai-status-completed-bg);color:var(--mm-ai-status-completed-fg)}.mm-ai-status-badge--failed{background:var(--mm-ai-status-failed-bg);color:var(--mm-ai-status-failed-fg)}.mm-ai-status-badge--blocked{background:var(--mm-ai-status-blocked-bg);color:var(--mm-ai-status-blocked-fg)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
388
388
|
}
|
|
389
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
389
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiJobStatusBadgeComponent, decorators: [{
|
|
390
390
|
type: Component,
|
|
391
391
|
args: [{ selector: 'mm-ai-job-status-badge', imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<span class=\"mm-ai-status-badge mm-ai-status-badge--{{ variant() }}\">\n {{ status() }}\n</span>\n", styles: [":host{display:inline-block;--mm-ai-status-running-bg: #1f6feb;--mm-ai-status-running-fg: #ffffff;--mm-ai-status-queued-bg: #6e7681;--mm-ai-status-queued-fg: #ffffff;--mm-ai-status-paused-bg: #d29922;--mm-ai-status-paused-fg: #1c1c1c;--mm-ai-status-completed-bg: #2ea44f;--mm-ai-status-completed-fg: #ffffff;--mm-ai-status-failed-bg: #d73a49;--mm-ai-status-failed-fg: #ffffff;--mm-ai-status-blocked-bg: #8957e5;--mm-ai-status-blocked-fg: #ffffff}.mm-ai-status-badge{display:inline-block;padding:.15rem .65rem;border-radius:9999px;font-size:.8rem;font-weight:600;line-height:1.2;letter-spacing:.01em}.mm-ai-status-badge--running{background:var(--mm-ai-status-running-bg);color:var(--mm-ai-status-running-fg)}.mm-ai-status-badge--queued{background:var(--mm-ai-status-queued-bg);color:var(--mm-ai-status-queued-fg)}.mm-ai-status-badge--paused{background:var(--mm-ai-status-paused-bg);color:var(--mm-ai-status-paused-fg)}.mm-ai-status-badge--completed{background:var(--mm-ai-status-completed-bg);color:var(--mm-ai-status-completed-fg)}.mm-ai-status-badge--failed{background:var(--mm-ai-status-failed-bg);color:var(--mm-ai-status-failed-fg)}.mm-ai-status-badge--blocked{background:var(--mm-ai-status-blocked-bg);color:var(--mm-ai-status-blocked-fg)}\n"] }]
|
|
392
392
|
}], propDecorators: { status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: true }] }] } });
|
|
@@ -458,10 +458,10 @@ class AiSessionListComponent {
|
|
|
458
458
|
const days = Math.floor(hours / 24);
|
|
459
459
|
return `${days}d ago`;
|
|
460
460
|
}
|
|
461
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
462
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
461
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiSessionListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
462
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AiSessionListComponent, isStandalone: true, selector: "mm-ai-session-list", inputs: { sessions: { classPropertyName: "sessions", publicName: "sessions", isSignal: true, isRequired: true, transformFunction: null }, selectedSessionId: { classPropertyName: "selectedSessionId", publicName: "selectedSessionId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sessionSelected: "sessionSelected", cancelRequested: "cancelRequested", deleteRequested: "deleteRequested" }, ngImport: i0, template: "<ul class=\"mm-ai-session-list\">\n @for (session of orderedSessions(); track session.sessionRtId) {\n <li\n class=\"mm-ai-session-list__row\"\n [class.mm-ai-session-list__row--selected]=\"\n session.sessionRtId === selectedSessionId()\n \"\n >\n <button\n type=\"button\"\n class=\"mm-ai-session-list__button\"\n (click)=\"onSelect(session.sessionRtId)\"\n >\n <!--\n Row layout: goal + status badge on top, meta + actions on the\n bottom. The two-line shape lets a narrow sidebar (Refinery\n Studio caps the column at 18rem) breathe \u2014 the previous\n horizontal cram had the goal text overlap the action buttons\n on every row.\n -->\n <div class=\"mm-ai-session-list__top\">\n <span class=\"mm-ai-session-list__goal\">{{ session.goalSummary }}</span>\n <mm-ai-job-status-badge [status]=\"session.status\" />\n </div>\n <div class=\"mm-ai-session-list__bottom\">\n <span class=\"mm-ai-session-list__meta\">\n {{ age(session.startedAt) }}\n <span class=\"mm-ai-session-list__dot\">\u00B7</span>\n {{ session.tokensConsumed }} tok\n </span>\n @if (session.previewUrl) {\n <!--\n #4136 \u2014 preview-deploy URL the server surfaces when the\n worker pod's nginx sidecar has a build to serve. Opens in\n a new tab so the sidebar scroll position survives.\n stopPropagation prevents the row click from also firing.\n -->\n <a\n class=\"mm-ai-session-list__preview\"\n [href]=\"session.previewUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n (click)=\"$event.stopPropagation()\"\n >\n Preview\n </a>\n }\n <span class=\"mm-ai-session-list__spacer\"></span>\n <span class=\"mm-ai-session-list__actions\">\n @if (canCancel(session.status)) {\n <button\n type=\"button\"\n class=\"mm-ai-session-list__action mm-ai-session-list__action--cancel\"\n [attr.aria-label]=\"'Cancel session ' + session.goalSummary\"\n (click)=\"onCancel(session.sessionRtId, $event)\"\n >\n Cancel\n </button>\n }\n @if (canDelete(session.status)) {\n <button\n type=\"button\"\n class=\"mm-ai-session-list__action mm-ai-session-list__action--delete\"\n [attr.aria-label]=\"'Delete session ' + session.goalSummary\"\n (click)=\"onDelete(session.sessionRtId, $event)\"\n >\n Delete\n </button>\n }\n </span>\n </div>\n </button>\n </li>\n } @empty {\n <li class=\"mm-ai-session-list__empty\">No sessions yet.</li>\n }\n</ul>\n", styles: [":host{display:block;--mm-ai-list-bg: #16191d;--mm-ai-list-row-bg: #1f2227;--mm-ai-list-row-selected: #2a3142;--mm-ai-list-border: #2f343b;--mm-ai-list-text: #e6e6e6;--mm-ai-list-muted: #9aa0a6;--mm-ai-list-accent: #64ceb9;--mm-ai-list-action-cancel: #da9162;--mm-ai-list-action-delete: #ec658f}.mm-ai-session-list{list-style:none;margin:0;padding:0;background:var(--mm-ai-list-bg);border-radius:.5rem;display:flex;flex-direction:column;gap:.25rem;padding:.25rem}.mm-ai-session-list__row{background:var(--mm-ai-list-row-bg);border:1px solid var(--mm-ai-list-border);border-radius:.4rem}.mm-ai-session-list__row--selected{background:var(--mm-ai-list-row-selected)}.mm-ai-session-list__button{width:100%;display:flex;flex-direction:column;gap:.3rem;padding:.5rem .75rem;background:transparent;color:var(--mm-ai-list-text);border:none;cursor:pointer;text-align:left;font-family:inherit;min-width:0}.mm-ai-session-list__button:hover{background:#ffffff08}.mm-ai-session-list__top{display:flex;align-items:center;gap:.5rem;min-width:0}.mm-ai-session-list__goal{flex:1;min-width:0;font-size:.85rem;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mm-ai-session-list__bottom{display:flex;align-items:center;gap:.5rem;font-size:.7rem;color:var(--mm-ai-list-muted);font-variant-numeric:tabular-nums;min-width:0}.mm-ai-session-list__meta{display:inline-flex;align-items:baseline;gap:.35rem;flex-shrink:0}.mm-ai-session-list__dot{opacity:.5}.mm-ai-session-list__spacer{flex:1}.mm-ai-session-list__preview{color:var(--mm-ai-list-accent);font-size:.7rem;text-decoration:underline;text-underline-offset:2px;flex-shrink:0}.mm-ai-session-list__preview:hover{text-decoration-thickness:2px}.mm-ai-session-list__empty{padding:1rem;text-align:center;color:var(--mm-ai-list-muted);font-style:italic}.mm-ai-session-list__actions{display:flex;gap:.25rem;flex-shrink:0;opacity:.45;transition:opacity .15s ease}.mm-ai-session-list__row:hover .mm-ai-session-list__actions,.mm-ai-session-list__row--selected .mm-ai-session-list__actions{opacity:1}.mm-ai-session-list__action{padding:.15rem .5rem;background:transparent;border:1px solid var(--mm-ai-list-border);border-radius:.25rem;color:var(--mm-ai-list-text);font-family:inherit;font-size:.65rem;letter-spacing:.04em;text-transform:uppercase;cursor:pointer;transition:border-color .15s ease,color .15s ease,background-color .15s ease}.mm-ai-session-list__action:hover{background:#ffffff0a}.mm-ai-session-list__action--cancel{color:var(--mm-ai-list-action-cancel);border-color:color-mix(in srgb,var(--mm-ai-list-action-cancel) 40%,transparent)}.mm-ai-session-list__action--cancel:hover{border-color:var(--mm-ai-list-action-cancel)}.mm-ai-session-list__action--delete{color:var(--mm-ai-list-action-delete);border-color:color-mix(in srgb,var(--mm-ai-list-action-delete) 40%,transparent)}.mm-ai-session-list__action--delete:hover{border-color:var(--mm-ai-list-action-delete)}\n"], dependencies: [{ kind: "component", type: AiJobStatusBadgeComponent, selector: "mm-ai-job-status-badge", inputs: ["status"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
463
463
|
}
|
|
464
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
464
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiSessionListComponent, decorators: [{
|
|
465
465
|
type: Component,
|
|
466
466
|
args: [{ selector: 'mm-ai-session-list', imports: [AiJobStatusBadgeComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ul class=\"mm-ai-session-list\">\n @for (session of orderedSessions(); track session.sessionRtId) {\n <li\n class=\"mm-ai-session-list__row\"\n [class.mm-ai-session-list__row--selected]=\"\n session.sessionRtId === selectedSessionId()\n \"\n >\n <button\n type=\"button\"\n class=\"mm-ai-session-list__button\"\n (click)=\"onSelect(session.sessionRtId)\"\n >\n <!--\n Row layout: goal + status badge on top, meta + actions on the\n bottom. The two-line shape lets a narrow sidebar (Refinery\n Studio caps the column at 18rem) breathe \u2014 the previous\n horizontal cram had the goal text overlap the action buttons\n on every row.\n -->\n <div class=\"mm-ai-session-list__top\">\n <span class=\"mm-ai-session-list__goal\">{{ session.goalSummary }}</span>\n <mm-ai-job-status-badge [status]=\"session.status\" />\n </div>\n <div class=\"mm-ai-session-list__bottom\">\n <span class=\"mm-ai-session-list__meta\">\n {{ age(session.startedAt) }}\n <span class=\"mm-ai-session-list__dot\">\u00B7</span>\n {{ session.tokensConsumed }} tok\n </span>\n @if (session.previewUrl) {\n <!--\n #4136 \u2014 preview-deploy URL the server surfaces when the\n worker pod's nginx sidecar has a build to serve. Opens in\n a new tab so the sidebar scroll position survives.\n stopPropagation prevents the row click from also firing.\n -->\n <a\n class=\"mm-ai-session-list__preview\"\n [href]=\"session.previewUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n (click)=\"$event.stopPropagation()\"\n >\n Preview\n </a>\n }\n <span class=\"mm-ai-session-list__spacer\"></span>\n <span class=\"mm-ai-session-list__actions\">\n @if (canCancel(session.status)) {\n <button\n type=\"button\"\n class=\"mm-ai-session-list__action mm-ai-session-list__action--cancel\"\n [attr.aria-label]=\"'Cancel session ' + session.goalSummary\"\n (click)=\"onCancel(session.sessionRtId, $event)\"\n >\n Cancel\n </button>\n }\n @if (canDelete(session.status)) {\n <button\n type=\"button\"\n class=\"mm-ai-session-list__action mm-ai-session-list__action--delete\"\n [attr.aria-label]=\"'Delete session ' + session.goalSummary\"\n (click)=\"onDelete(session.sessionRtId, $event)\"\n >\n Delete\n </button>\n }\n </span>\n </div>\n </button>\n </li>\n } @empty {\n <li class=\"mm-ai-session-list__empty\">No sessions yet.</li>\n }\n</ul>\n", styles: [":host{display:block;--mm-ai-list-bg: #16191d;--mm-ai-list-row-bg: #1f2227;--mm-ai-list-row-selected: #2a3142;--mm-ai-list-border: #2f343b;--mm-ai-list-text: #e6e6e6;--mm-ai-list-muted: #9aa0a6;--mm-ai-list-accent: #64ceb9;--mm-ai-list-action-cancel: #da9162;--mm-ai-list-action-delete: #ec658f}.mm-ai-session-list{list-style:none;margin:0;padding:0;background:var(--mm-ai-list-bg);border-radius:.5rem;display:flex;flex-direction:column;gap:.25rem;padding:.25rem}.mm-ai-session-list__row{background:var(--mm-ai-list-row-bg);border:1px solid var(--mm-ai-list-border);border-radius:.4rem}.mm-ai-session-list__row--selected{background:var(--mm-ai-list-row-selected)}.mm-ai-session-list__button{width:100%;display:flex;flex-direction:column;gap:.3rem;padding:.5rem .75rem;background:transparent;color:var(--mm-ai-list-text);border:none;cursor:pointer;text-align:left;font-family:inherit;min-width:0}.mm-ai-session-list__button:hover{background:#ffffff08}.mm-ai-session-list__top{display:flex;align-items:center;gap:.5rem;min-width:0}.mm-ai-session-list__goal{flex:1;min-width:0;font-size:.85rem;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mm-ai-session-list__bottom{display:flex;align-items:center;gap:.5rem;font-size:.7rem;color:var(--mm-ai-list-muted);font-variant-numeric:tabular-nums;min-width:0}.mm-ai-session-list__meta{display:inline-flex;align-items:baseline;gap:.35rem;flex-shrink:0}.mm-ai-session-list__dot{opacity:.5}.mm-ai-session-list__spacer{flex:1}.mm-ai-session-list__preview{color:var(--mm-ai-list-accent);font-size:.7rem;text-decoration:underline;text-underline-offset:2px;flex-shrink:0}.mm-ai-session-list__preview:hover{text-decoration-thickness:2px}.mm-ai-session-list__empty{padding:1rem;text-align:center;color:var(--mm-ai-list-muted);font-style:italic}.mm-ai-session-list__actions{display:flex;gap:.25rem;flex-shrink:0;opacity:.45;transition:opacity .15s ease}.mm-ai-session-list__row:hover .mm-ai-session-list__actions,.mm-ai-session-list__row--selected .mm-ai-session-list__actions{opacity:1}.mm-ai-session-list__action{padding:.15rem .5rem;background:transparent;border:1px solid var(--mm-ai-list-border);border-radius:.25rem;color:var(--mm-ai-list-text);font-family:inherit;font-size:.65rem;letter-spacing:.04em;text-transform:uppercase;cursor:pointer;transition:border-color .15s ease,color .15s ease,background-color .15s ease}.mm-ai-session-list__action:hover{background:#ffffff0a}.mm-ai-session-list__action--cancel{color:var(--mm-ai-list-action-cancel);border-color:color-mix(in srgb,var(--mm-ai-list-action-cancel) 40%,transparent)}.mm-ai-session-list__action--cancel:hover{border-color:var(--mm-ai-list-action-cancel)}.mm-ai-session-list__action--delete{color:var(--mm-ai-list-action-delete);border-color:color-mix(in srgb,var(--mm-ai-list-action-delete) 40%,transparent)}.mm-ai-session-list__action--delete:hover{border-color:var(--mm-ai-list-action-delete)}\n"] }]
|
|
467
467
|
}], propDecorators: { sessions: [{ type: i0.Input, args: [{ isSignal: true, alias: "sessions", required: true }] }], selectedSessionId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedSessionId", required: false }] }], sessionSelected: [{ type: i0.Output, args: ["sessionSelected"] }], cancelRequested: [{ type: i0.Output, args: ["cancelRequested"] }], deleteRequested: [{ type: i0.Output, args: ["deleteRequested"] }] } });
|
|
@@ -866,10 +866,10 @@ class AiChatStreamComponent {
|
|
|
866
866
|
return String(value);
|
|
867
867
|
}
|
|
868
868
|
}
|
|
869
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
870
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AiChatStreamComponent, isStandalone: true, selector: "mm-ai-chat-stream", inputs: { events: { classPropertyName: "events", publicName: "events", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"mm-ai-chat\">\n @for (parsed of parsedEvents(); track parsed.event.sequence) {\n @switch (parsed.view.kind) {\n @case ('assistant') {\n <article class=\"mm-ai-chat__bubble mm-ai-chat__bubble--assistant\">\n <header class=\"mm-ai-chat__bubble-header\">\n <span class=\"mm-ai-chat__role\">Assistant</span>\n @if (parsed.view.model; as model) {\n <span class=\"mm-ai-chat__meta\">{{ model }}</span>\n }\n @if (parsed.view.usage; as usage) {\n <span class=\"mm-ai-chat__meta\">\n {{ formatTokenCount(usage.input + usage.output) }}\n </span>\n }\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </header>\n @for (part of parsed.view.parts; track $index) {\n @switch (part.type) {\n @case ('text') {\n <p class=\"mm-ai-chat__text\">{{ part.text }}</p>\n }\n @case ('tool_use') {\n <div class=\"mm-ai-chat__tool-use\">\n <header class=\"mm-ai-chat__tool-use-header\">\n <span class=\"mm-ai-chat__tool-badge\">tool</span>\n <span class=\"mm-ai-chat__tool-name\">{{ part.toolName }}</span>\n </header>\n <pre class=\"mm-ai-chat__tool-input\">{{ formatRaw(part.input) }}</pre>\n </div>\n }\n }\n }\n </article>\n }\n\n @case ('tool-result') {\n <article\n class=\"mm-ai-chat__tool-result\"\n [class.mm-ai-chat__tool-result--error]=\"parsed.view.isError\"\n >\n <header class=\"mm-ai-chat__tool-result-header\">\n <span class=\"mm-ai-chat__role\">Tool result</span>\n @if (parsed.view.isError) {\n <span class=\"mm-ai-chat__error-badge\">error</span>\n }\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </header>\n <pre class=\"mm-ai-chat__tool-result-body\">{{ parsed.view.content }}</pre>\n </article>\n }\n\n @case ('result') {\n <article class=\"mm-ai-chat__result\"\n [class.mm-ai-chat__result--error]=\"parsed.view.isError\">\n <header class=\"mm-ai-chat__result-header\">\n <span class=\"mm-ai-chat__role\">{{ parsed.view.reason }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </header>\n @if (parsed.view.text) {\n <p class=\"mm-ai-chat__result-text\">{{ parsed.view.text }}</p>\n }\n <dl class=\"mm-ai-chat__result-stats\">\n <div>\n <dt>Duration</dt>\n <dd>{{ formatDuration(parsed.view.durationMs) }}</dd>\n </div>\n <div>\n <dt>Tokens (in/out)</dt>\n <dd>\n @if (parsed.view.usage) {\n {{ formatTokenCount(parsed.view.usage.input) }}\n /\n {{ formatTokenCount(parsed.view.usage.output) }}\n } @else {\n \u2014\n }\n </dd>\n </div>\n <div>\n <dt>Cost</dt>\n <dd>{{ formatCost(parsed.view.costUsd) }}</dd>\n </div>\n </dl>\n </article>\n }\n\n @case ('system') {\n <button\n type=\"button\"\n class=\"mm-ai-chat__system\"\n [class.mm-ai-chat__system--init]=\"parsed.view.subKind === 'init'\"\n [class.mm-ai-chat__system--hook]=\"parsed.view.subKind === 'hook'\"\n [class.mm-ai-chat__system--rate]=\"parsed.view.subKind === 'rate-limit'\"\n [attr.aria-expanded]=\"isExpanded(parsed.event.sequence)\"\n (click)=\"toggleRaw(parsed.event.sequence)\"\n >\n <span class=\"mm-ai-chat__system-chevron\">\n {{ isExpanded(parsed.event.sequence) ? '\u25BE' : '\u25B8' }}\n </span>\n <span class=\"mm-ai-chat__system-summary\">{{ parsed.view.summary }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </button>\n @if (isExpanded(parsed.event.sequence)) {\n <pre class=\"mm-ai-chat__system-detail\">{{ formatRaw(parsed.view.raw) }}</pre>\n }\n }\n\n @case ('status-change') {\n <div class=\"mm-ai-chat__status\">\n <span class=\"mm-ai-chat__status-label\">Status</span>\n <span class=\"mm-ai-chat__status-value\">{{ parsed.view.status }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </div>\n }\n\n @case ('unknown') {\n <button\n type=\"button\"\n class=\"mm-ai-chat__system mm-ai-chat__system--unknown\"\n [attr.aria-expanded]=\"isExpanded(parsed.event.sequence)\"\n (click)=\"toggleRaw(parsed.event.sequence)\"\n >\n <span class=\"mm-ai-chat__system-chevron\">\n {{ isExpanded(parsed.event.sequence) ? '\u25BE' : '\u25B8' }}\n </span>\n <span class=\"mm-ai-chat__system-summary\">{{ parsed.view.summary }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </button>\n @if (isExpanded(parsed.event.sequence)) {\n <pre class=\"mm-ai-chat__system-detail\">{{ formatRaw(parsed.view.raw) }}</pre>\n }\n }\n }\n } @empty {\n <div class=\"mm-ai-chat__empty\">\n No events yet \u2014 the worker hasn't emitted anything.\n </div>\n }\n</div>\n", styles: [":host{display:block;--mm-ai-chat-bg: transparent;--mm-ai-chat-card-bg: rgba(31, 46, 64, .55);--mm-ai-chat-card-bg-elevated: rgba(31, 46, 64, .85);--mm-ai-chat-border: rgba(100, 206, 185, .18);--mm-ai-chat-border-strong: rgba(100, 206, 185, .4);--mm-ai-chat-text: #e6e6e6;--mm-ai-chat-muted: #9292a6;--mm-ai-chat-accent-assistant: #64ceb9;--mm-ai-chat-accent-tool: #00a8dc;--mm-ai-chat-accent-tool-result: #00a8dc;--mm-ai-chat-accent-system: #6c4da8;--mm-ai-chat-accent-rate-limit: #da9162;--mm-ai-chat-accent-result: #64ceb9;--mm-ai-chat-accent-error: #ec658f}.mm-ai-chat{display:flex;flex-direction:column;gap:.5rem;padding:.25rem;background:var(--mm-ai-chat-bg);color:var(--mm-ai-chat-text);font-size:.85rem;line-height:1.45}.mm-ai-chat__empty{color:var(--mm-ai-chat-muted);text-align:center;padding:1.5rem;font-style:italic}.mm-ai-chat__bubble{background:var(--mm-ai-chat-card-bg);border:1px solid var(--mm-ai-chat-border);border-left:3px solid var(--mm-ai-chat-accent-assistant);border-radius:.5rem;padding:.6rem .75rem;display:flex;flex-direction:column;gap:.5rem}.mm-ai-chat__bubble-header{display:flex;align-items:baseline;gap:.5rem;font-size:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__role{font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--mm-ai-chat-accent-assistant)}.mm-ai-chat__meta{font-family:Roboto Mono,monospace;font-size:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__time{margin-left:auto;font-variant-numeric:tabular-nums;font-size:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__text{margin:0;white-space:pre-wrap;word-break:break-word}.mm-ai-chat__tool-use{border:1px solid var(--mm-ai-chat-border);border-left:2px solid var(--mm-ai-chat-accent-tool);border-radius:.35rem;background:var(--mm-ai-chat-card-bg-elevated);padding:.4rem .5rem}.mm-ai-chat__tool-use-header{display:flex;align-items:baseline;gap:.4rem;font-size:.7rem}.mm-ai-chat__tool-badge{background:var(--mm-ai-chat-accent-tool);color:#07172b;padding:0 6px;border-radius:.25rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;font-size:.65rem}.mm-ai-chat__tool-name{font-family:Roboto Mono,monospace;color:var(--mm-ai-chat-text);font-weight:600}.mm-ai-chat__tool-input{margin:.35rem 0 0;font-family:Roboto Mono,monospace;font-size:.75rem;white-space:pre-wrap;word-break:break-word;color:var(--mm-ai-chat-muted);max-height:14rem;overflow-y:auto}.mm-ai-chat__tool-result{background:var(--mm-ai-chat-card-bg);border:1px solid var(--mm-ai-chat-border);border-left:3px solid var(--mm-ai-chat-accent-tool-result);border-radius:.5rem;padding:.55rem .75rem;margin-left:1.25rem}.mm-ai-chat__tool-result--error{border-left-color:var(--mm-ai-chat-accent-error)}.mm-ai-chat__tool-result-header{display:flex;align-items:baseline;gap:.5rem;font-size:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__tool-result-header .mm-ai-chat__role{color:var(--mm-ai-chat-accent-tool-result)}.mm-ai-chat__error-badge{background:var(--mm-ai-chat-accent-error);color:#07172b;padding:0 6px;border-radius:.25rem;font-size:.6rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase}.mm-ai-chat__tool-result-body{margin:.4rem 0 0;font-family:Roboto Mono,monospace;font-size:.75rem;white-space:pre-wrap;word-break:break-word;max-height:18rem;overflow-y:auto}.mm-ai-chat__result{background:var(--mm-ai-chat-card-bg-elevated);border:1px solid var(--mm-ai-chat-border-strong);border-top:3px solid var(--mm-ai-chat-accent-result);border-radius:.5rem;padding:.7rem .85rem;display:flex;flex-direction:column;gap:.5rem}.mm-ai-chat__result--error{border-top-color:var(--mm-ai-chat-accent-error)}.mm-ai-chat__result-header{display:flex;align-items:baseline;gap:.5rem;font-size:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__result-header .mm-ai-chat__role{color:var(--mm-ai-chat-accent-result);text-transform:uppercase}.mm-ai-chat__result-text{margin:0;white-space:pre-wrap;word-break:break-word;font-style:italic;color:var(--mm-ai-chat-text)}.mm-ai-chat__result-stats{margin:0;display:grid;grid-template-columns:repeat(auto-fit,minmax(7rem,1fr));gap:.5rem;font-family:Roboto Mono,monospace;font-size:.75rem}.mm-ai-chat__result-stats div{display:flex;flex-direction:column;gap:.2rem;padding:.4rem .5rem;background:#64ceb90f;border-radius:.3rem}.mm-ai-chat__result-stats dt{font-size:.62rem;text-transform:uppercase;letter-spacing:.08em;color:var(--mm-ai-chat-muted)}.mm-ai-chat__result-stats dd{margin:0;color:var(--mm-ai-chat-text);font-variant-numeric:tabular-nums}.mm-ai-chat__system{all:unset;cursor:pointer;display:flex;align-items:center;gap:.5rem;padding:.35rem .6rem;background:#64ceb908;border:1px solid var(--mm-ai-chat-border);border-left:2px solid var(--mm-ai-chat-accent-system);border-radius:.4rem;font-size:.78rem;color:var(--mm-ai-chat-muted);transition:background-color .15s ease}.mm-ai-chat__system:hover{background:#64ceb90f}.mm-ai-chat__system:focus-visible{outline:2px solid var(--mm-ai-chat-accent-assistant);outline-offset:1px}.mm-ai-chat__system--init{border-left-color:var(--mm-ai-chat-accent-tool)}.mm-ai-chat__system--hook{border-left-color:var(--mm-ai-chat-accent-system)}.mm-ai-chat__system--rate{border-left-color:var(--mm-ai-chat-accent-rate-limit)}.mm-ai-chat__system--unknown{border-left-color:var(--mm-ai-chat-accent-error)}.mm-ai-chat__system-chevron{font-size:.7rem;width:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__system-summary{flex:1;color:var(--mm-ai-chat-text);font-variant-numeric:tabular-nums}.mm-ai-chat__system-detail{margin:-.25rem 0 0 1.5rem;padding:.5rem .6rem;background:#00000040;border-radius:.35rem;font-family:Roboto Mono,monospace;font-size:.7rem;white-space:pre-wrap;word-break:break-word;max-height:22rem;overflow-y:auto;color:var(--mm-ai-chat-muted)}.mm-ai-chat__status{display:flex;align-items:baseline;gap:.5rem;padding:.25rem .5rem;font-size:.7rem;color:var(--mm-ai-chat-muted);text-transform:uppercase;letter-spacing:.06em}.mm-ai-chat__status-label{color:var(--mm-ai-chat-muted)}.mm-ai-chat__status-value{color:var(--mm-ai-chat-text);font-weight:600}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
869
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiChatStreamComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
870
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AiChatStreamComponent, isStandalone: true, selector: "mm-ai-chat-stream", inputs: { events: { classPropertyName: "events", publicName: "events", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"mm-ai-chat\">\n @for (parsed of parsedEvents(); track parsed.event.sequence) {\n @switch (parsed.view.kind) {\n @case ('assistant') {\n <article class=\"mm-ai-chat__bubble mm-ai-chat__bubble--assistant\">\n <header class=\"mm-ai-chat__bubble-header\">\n <span class=\"mm-ai-chat__role\">Assistant</span>\n @if (parsed.view.model; as model) {\n <span class=\"mm-ai-chat__meta\">{{ model }}</span>\n }\n @if (parsed.view.usage; as usage) {\n <span class=\"mm-ai-chat__meta\">\n {{ formatTokenCount(usage.input + usage.output) }}\n </span>\n }\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </header>\n @for (part of parsed.view.parts; track $index) {\n @switch (part.type) {\n @case ('text') {\n <p class=\"mm-ai-chat__text\">{{ part.text }}</p>\n }\n @case ('tool_use') {\n <div class=\"mm-ai-chat__tool-use\">\n <header class=\"mm-ai-chat__tool-use-header\">\n <span class=\"mm-ai-chat__tool-badge\">tool</span>\n <span class=\"mm-ai-chat__tool-name\">{{ part.toolName }}</span>\n </header>\n <pre class=\"mm-ai-chat__tool-input\">{{ formatRaw(part.input) }}</pre>\n </div>\n }\n }\n }\n </article>\n }\n\n @case ('tool-result') {\n <article\n class=\"mm-ai-chat__tool-result\"\n [class.mm-ai-chat__tool-result--error]=\"parsed.view.isError\"\n >\n <header class=\"mm-ai-chat__tool-result-header\">\n <span class=\"mm-ai-chat__role\">Tool result</span>\n @if (parsed.view.isError) {\n <span class=\"mm-ai-chat__error-badge\">error</span>\n }\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </header>\n <pre class=\"mm-ai-chat__tool-result-body\">{{ parsed.view.content }}</pre>\n </article>\n }\n\n @case ('result') {\n <article class=\"mm-ai-chat__result\"\n [class.mm-ai-chat__result--error]=\"parsed.view.isError\">\n <header class=\"mm-ai-chat__result-header\">\n <span class=\"mm-ai-chat__role\">{{ parsed.view.reason }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </header>\n @if (parsed.view.text) {\n <p class=\"mm-ai-chat__result-text\">{{ parsed.view.text }}</p>\n }\n <dl class=\"mm-ai-chat__result-stats\">\n <div>\n <dt>Duration</dt>\n <dd>{{ formatDuration(parsed.view.durationMs) }}</dd>\n </div>\n <div>\n <dt>Tokens (in/out)</dt>\n <dd>\n @if (parsed.view.usage) {\n {{ formatTokenCount(parsed.view.usage.input) }}\n /\n {{ formatTokenCount(parsed.view.usage.output) }}\n } @else {\n \u2014\n }\n </dd>\n </div>\n <div>\n <dt>Cost</dt>\n <dd>{{ formatCost(parsed.view.costUsd) }}</dd>\n </div>\n </dl>\n </article>\n }\n\n @case ('system') {\n <button\n type=\"button\"\n class=\"mm-ai-chat__system\"\n [class.mm-ai-chat__system--init]=\"parsed.view.subKind === 'init'\"\n [class.mm-ai-chat__system--hook]=\"parsed.view.subKind === 'hook'\"\n [class.mm-ai-chat__system--rate]=\"parsed.view.subKind === 'rate-limit'\"\n [attr.aria-expanded]=\"isExpanded(parsed.event.sequence)\"\n (click)=\"toggleRaw(parsed.event.sequence)\"\n >\n <span class=\"mm-ai-chat__system-chevron\">\n {{ isExpanded(parsed.event.sequence) ? '\u25BE' : '\u25B8' }}\n </span>\n <span class=\"mm-ai-chat__system-summary\">{{ parsed.view.summary }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </button>\n @if (isExpanded(parsed.event.sequence)) {\n <pre class=\"mm-ai-chat__system-detail\">{{ formatRaw(parsed.view.raw) }}</pre>\n }\n }\n\n @case ('status-change') {\n <div class=\"mm-ai-chat__status\">\n <span class=\"mm-ai-chat__status-label\">Status</span>\n <span class=\"mm-ai-chat__status-value\">{{ parsed.view.status }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </div>\n }\n\n @case ('unknown') {\n <button\n type=\"button\"\n class=\"mm-ai-chat__system mm-ai-chat__system--unknown\"\n [attr.aria-expanded]=\"isExpanded(parsed.event.sequence)\"\n (click)=\"toggleRaw(parsed.event.sequence)\"\n >\n <span class=\"mm-ai-chat__system-chevron\">\n {{ isExpanded(parsed.event.sequence) ? '\u25BE' : '\u25B8' }}\n </span>\n <span class=\"mm-ai-chat__system-summary\">{{ parsed.view.summary }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </button>\n @if (isExpanded(parsed.event.sequence)) {\n <pre class=\"mm-ai-chat__system-detail\">{{ formatRaw(parsed.view.raw) }}</pre>\n }\n }\n }\n } @empty {\n <div class=\"mm-ai-chat__empty\">\n No events yet \u2014 the worker hasn't emitted anything.\n </div>\n }\n</div>\n", styles: [":host{display:block;--mm-ai-chat-bg: transparent;--mm-ai-chat-card-bg: rgba(31, 46, 64, .55);--mm-ai-chat-card-bg-elevated: rgba(31, 46, 64, .85);--mm-ai-chat-border: rgba(100, 206, 185, .18);--mm-ai-chat-border-strong: rgba(100, 206, 185, .4);--mm-ai-chat-text: #e6e6e6;--mm-ai-chat-muted: #9292a6;--mm-ai-chat-accent-assistant: #64ceb9;--mm-ai-chat-accent-tool: #00a8dc;--mm-ai-chat-accent-tool-result: #00a8dc;--mm-ai-chat-accent-system: #6c4da8;--mm-ai-chat-accent-rate-limit: #da9162;--mm-ai-chat-accent-result: #64ceb9;--mm-ai-chat-accent-error: #ec658f}.mm-ai-chat{display:flex;flex-direction:column;gap:.5rem;padding:.25rem;background:var(--mm-ai-chat-bg);color:var(--mm-ai-chat-text);font-size:.85rem;line-height:1.45}.mm-ai-chat__empty{color:var(--mm-ai-chat-muted);text-align:center;padding:1.5rem;font-style:italic}.mm-ai-chat__bubble{background:var(--mm-ai-chat-card-bg);border:1px solid var(--mm-ai-chat-border);border-left:3px solid var(--mm-ai-chat-accent-assistant);border-radius:.5rem;padding:.6rem .75rem;display:flex;flex-direction:column;gap:.5rem}.mm-ai-chat__bubble-header{display:flex;align-items:baseline;gap:.5rem;font-size:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__role{font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--mm-ai-chat-accent-assistant)}.mm-ai-chat__meta{font-family:Roboto Mono,monospace;font-size:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__time{margin-left:auto;font-variant-numeric:tabular-nums;font-size:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__text{margin:0;white-space:pre-wrap;word-break:break-word}.mm-ai-chat__tool-use{border:1px solid var(--mm-ai-chat-border);border-left:2px solid var(--mm-ai-chat-accent-tool);border-radius:.35rem;background:var(--mm-ai-chat-card-bg-elevated);padding:.4rem .5rem}.mm-ai-chat__tool-use-header{display:flex;align-items:baseline;gap:.4rem;font-size:.7rem}.mm-ai-chat__tool-badge{background:var(--mm-ai-chat-accent-tool);color:#07172b;padding:0 6px;border-radius:.25rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;font-size:.65rem}.mm-ai-chat__tool-name{font-family:Roboto Mono,monospace;color:var(--mm-ai-chat-text);font-weight:600}.mm-ai-chat__tool-input{margin:.35rem 0 0;font-family:Roboto Mono,monospace;font-size:.75rem;white-space:pre-wrap;word-break:break-word;color:var(--mm-ai-chat-muted);max-height:14rem;overflow-y:auto}.mm-ai-chat__tool-result{background:var(--mm-ai-chat-card-bg);border:1px solid var(--mm-ai-chat-border);border-left:3px solid var(--mm-ai-chat-accent-tool-result);border-radius:.5rem;padding:.55rem .75rem;margin-left:1.25rem}.mm-ai-chat__tool-result--error{border-left-color:var(--mm-ai-chat-accent-error)}.mm-ai-chat__tool-result-header{display:flex;align-items:baseline;gap:.5rem;font-size:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__tool-result-header .mm-ai-chat__role{color:var(--mm-ai-chat-accent-tool-result)}.mm-ai-chat__error-badge{background:var(--mm-ai-chat-accent-error);color:#07172b;padding:0 6px;border-radius:.25rem;font-size:.6rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase}.mm-ai-chat__tool-result-body{margin:.4rem 0 0;font-family:Roboto Mono,monospace;font-size:.75rem;white-space:pre-wrap;word-break:break-word;max-height:18rem;overflow-y:auto}.mm-ai-chat__result{background:var(--mm-ai-chat-card-bg-elevated);border:1px solid var(--mm-ai-chat-border-strong);border-top:3px solid var(--mm-ai-chat-accent-result);border-radius:.5rem;padding:.7rem .85rem;display:flex;flex-direction:column;gap:.5rem}.mm-ai-chat__result--error{border-top-color:var(--mm-ai-chat-accent-error)}.mm-ai-chat__result-header{display:flex;align-items:baseline;gap:.5rem;font-size:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__result-header .mm-ai-chat__role{color:var(--mm-ai-chat-accent-result);text-transform:uppercase}.mm-ai-chat__result-text{margin:0;white-space:pre-wrap;word-break:break-word;font-style:italic;color:var(--mm-ai-chat-text)}.mm-ai-chat__result-stats{margin:0;display:grid;grid-template-columns:repeat(auto-fit,minmax(7rem,1fr));gap:.5rem;font-family:Roboto Mono,monospace;font-size:.75rem}.mm-ai-chat__result-stats div{display:flex;flex-direction:column;gap:.2rem;padding:.4rem .5rem;background:#64ceb90f;border-radius:.3rem}.mm-ai-chat__result-stats dt{font-size:.62rem;text-transform:uppercase;letter-spacing:.08em;color:var(--mm-ai-chat-muted)}.mm-ai-chat__result-stats dd{margin:0;color:var(--mm-ai-chat-text);font-variant-numeric:tabular-nums}.mm-ai-chat__system{all:unset;cursor:pointer;display:flex;align-items:center;gap:.5rem;padding:.35rem .6rem;background:#64ceb908;border:1px solid var(--mm-ai-chat-border);border-left:2px solid var(--mm-ai-chat-accent-system);border-radius:.4rem;font-size:.78rem;color:var(--mm-ai-chat-muted);transition:background-color .15s ease}.mm-ai-chat__system:hover{background:#64ceb90f}.mm-ai-chat__system:focus-visible{outline:2px solid var(--mm-ai-chat-accent-assistant);outline-offset:1px}.mm-ai-chat__system--init{border-left-color:var(--mm-ai-chat-accent-tool)}.mm-ai-chat__system--hook{border-left-color:var(--mm-ai-chat-accent-system)}.mm-ai-chat__system--rate{border-left-color:var(--mm-ai-chat-accent-rate-limit)}.mm-ai-chat__system--unknown{border-left-color:var(--mm-ai-chat-accent-error)}.mm-ai-chat__system-chevron{font-size:.7rem;width:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__system-summary{flex:1;color:var(--mm-ai-chat-text);font-variant-numeric:tabular-nums}.mm-ai-chat__system-detail{margin:-.25rem 0 0 1.5rem;padding:.5rem .6rem;background:#00000040;border-radius:.35rem;font-family:Roboto Mono,monospace;font-size:.7rem;white-space:pre-wrap;word-break:break-word;max-height:22rem;overflow-y:auto;color:var(--mm-ai-chat-muted)}.mm-ai-chat__status{display:flex;align-items:baseline;gap:.5rem;padding:.25rem .5rem;font-size:.7rem;color:var(--mm-ai-chat-muted);text-transform:uppercase;letter-spacing:.06em}.mm-ai-chat__status-label{color:var(--mm-ai-chat-muted)}.mm-ai-chat__status-value{color:var(--mm-ai-chat-text);font-weight:600}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
871
871
|
}
|
|
872
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
872
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiChatStreamComponent, decorators: [{
|
|
873
873
|
type: Component,
|
|
874
874
|
args: [{ selector: 'mm-ai-chat-stream', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mm-ai-chat\">\n @for (parsed of parsedEvents(); track parsed.event.sequence) {\n @switch (parsed.view.kind) {\n @case ('assistant') {\n <article class=\"mm-ai-chat__bubble mm-ai-chat__bubble--assistant\">\n <header class=\"mm-ai-chat__bubble-header\">\n <span class=\"mm-ai-chat__role\">Assistant</span>\n @if (parsed.view.model; as model) {\n <span class=\"mm-ai-chat__meta\">{{ model }}</span>\n }\n @if (parsed.view.usage; as usage) {\n <span class=\"mm-ai-chat__meta\">\n {{ formatTokenCount(usage.input + usage.output) }}\n </span>\n }\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </header>\n @for (part of parsed.view.parts; track $index) {\n @switch (part.type) {\n @case ('text') {\n <p class=\"mm-ai-chat__text\">{{ part.text }}</p>\n }\n @case ('tool_use') {\n <div class=\"mm-ai-chat__tool-use\">\n <header class=\"mm-ai-chat__tool-use-header\">\n <span class=\"mm-ai-chat__tool-badge\">tool</span>\n <span class=\"mm-ai-chat__tool-name\">{{ part.toolName }}</span>\n </header>\n <pre class=\"mm-ai-chat__tool-input\">{{ formatRaw(part.input) }}</pre>\n </div>\n }\n }\n }\n </article>\n }\n\n @case ('tool-result') {\n <article\n class=\"mm-ai-chat__tool-result\"\n [class.mm-ai-chat__tool-result--error]=\"parsed.view.isError\"\n >\n <header class=\"mm-ai-chat__tool-result-header\">\n <span class=\"mm-ai-chat__role\">Tool result</span>\n @if (parsed.view.isError) {\n <span class=\"mm-ai-chat__error-badge\">error</span>\n }\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </header>\n <pre class=\"mm-ai-chat__tool-result-body\">{{ parsed.view.content }}</pre>\n </article>\n }\n\n @case ('result') {\n <article class=\"mm-ai-chat__result\"\n [class.mm-ai-chat__result--error]=\"parsed.view.isError\">\n <header class=\"mm-ai-chat__result-header\">\n <span class=\"mm-ai-chat__role\">{{ parsed.view.reason }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </header>\n @if (parsed.view.text) {\n <p class=\"mm-ai-chat__result-text\">{{ parsed.view.text }}</p>\n }\n <dl class=\"mm-ai-chat__result-stats\">\n <div>\n <dt>Duration</dt>\n <dd>{{ formatDuration(parsed.view.durationMs) }}</dd>\n </div>\n <div>\n <dt>Tokens (in/out)</dt>\n <dd>\n @if (parsed.view.usage) {\n {{ formatTokenCount(parsed.view.usage.input) }}\n /\n {{ formatTokenCount(parsed.view.usage.output) }}\n } @else {\n \u2014\n }\n </dd>\n </div>\n <div>\n <dt>Cost</dt>\n <dd>{{ formatCost(parsed.view.costUsd) }}</dd>\n </div>\n </dl>\n </article>\n }\n\n @case ('system') {\n <button\n type=\"button\"\n class=\"mm-ai-chat__system\"\n [class.mm-ai-chat__system--init]=\"parsed.view.subKind === 'init'\"\n [class.mm-ai-chat__system--hook]=\"parsed.view.subKind === 'hook'\"\n [class.mm-ai-chat__system--rate]=\"parsed.view.subKind === 'rate-limit'\"\n [attr.aria-expanded]=\"isExpanded(parsed.event.sequence)\"\n (click)=\"toggleRaw(parsed.event.sequence)\"\n >\n <span class=\"mm-ai-chat__system-chevron\">\n {{ isExpanded(parsed.event.sequence) ? '\u25BE' : '\u25B8' }}\n </span>\n <span class=\"mm-ai-chat__system-summary\">{{ parsed.view.summary }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </button>\n @if (isExpanded(parsed.event.sequence)) {\n <pre class=\"mm-ai-chat__system-detail\">{{ formatRaw(parsed.view.raw) }}</pre>\n }\n }\n\n @case ('status-change') {\n <div class=\"mm-ai-chat__status\">\n <span class=\"mm-ai-chat__status-label\">Status</span>\n <span class=\"mm-ai-chat__status-value\">{{ parsed.view.status }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </div>\n }\n\n @case ('unknown') {\n <button\n type=\"button\"\n class=\"mm-ai-chat__system mm-ai-chat__system--unknown\"\n [attr.aria-expanded]=\"isExpanded(parsed.event.sequence)\"\n (click)=\"toggleRaw(parsed.event.sequence)\"\n >\n <span class=\"mm-ai-chat__system-chevron\">\n {{ isExpanded(parsed.event.sequence) ? '\u25BE' : '\u25B8' }}\n </span>\n <span class=\"mm-ai-chat__system-summary\">{{ parsed.view.summary }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </button>\n @if (isExpanded(parsed.event.sequence)) {\n <pre class=\"mm-ai-chat__system-detail\">{{ formatRaw(parsed.view.raw) }}</pre>\n }\n }\n }\n } @empty {\n <div class=\"mm-ai-chat__empty\">\n No events yet \u2014 the worker hasn't emitted anything.\n </div>\n }\n</div>\n", styles: [":host{display:block;--mm-ai-chat-bg: transparent;--mm-ai-chat-card-bg: rgba(31, 46, 64, .55);--mm-ai-chat-card-bg-elevated: rgba(31, 46, 64, .85);--mm-ai-chat-border: rgba(100, 206, 185, .18);--mm-ai-chat-border-strong: rgba(100, 206, 185, .4);--mm-ai-chat-text: #e6e6e6;--mm-ai-chat-muted: #9292a6;--mm-ai-chat-accent-assistant: #64ceb9;--mm-ai-chat-accent-tool: #00a8dc;--mm-ai-chat-accent-tool-result: #00a8dc;--mm-ai-chat-accent-system: #6c4da8;--mm-ai-chat-accent-rate-limit: #da9162;--mm-ai-chat-accent-result: #64ceb9;--mm-ai-chat-accent-error: #ec658f}.mm-ai-chat{display:flex;flex-direction:column;gap:.5rem;padding:.25rem;background:var(--mm-ai-chat-bg);color:var(--mm-ai-chat-text);font-size:.85rem;line-height:1.45}.mm-ai-chat__empty{color:var(--mm-ai-chat-muted);text-align:center;padding:1.5rem;font-style:italic}.mm-ai-chat__bubble{background:var(--mm-ai-chat-card-bg);border:1px solid var(--mm-ai-chat-border);border-left:3px solid var(--mm-ai-chat-accent-assistant);border-radius:.5rem;padding:.6rem .75rem;display:flex;flex-direction:column;gap:.5rem}.mm-ai-chat__bubble-header{display:flex;align-items:baseline;gap:.5rem;font-size:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__role{font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--mm-ai-chat-accent-assistant)}.mm-ai-chat__meta{font-family:Roboto Mono,monospace;font-size:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__time{margin-left:auto;font-variant-numeric:tabular-nums;font-size:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__text{margin:0;white-space:pre-wrap;word-break:break-word}.mm-ai-chat__tool-use{border:1px solid var(--mm-ai-chat-border);border-left:2px solid var(--mm-ai-chat-accent-tool);border-radius:.35rem;background:var(--mm-ai-chat-card-bg-elevated);padding:.4rem .5rem}.mm-ai-chat__tool-use-header{display:flex;align-items:baseline;gap:.4rem;font-size:.7rem}.mm-ai-chat__tool-badge{background:var(--mm-ai-chat-accent-tool);color:#07172b;padding:0 6px;border-radius:.25rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;font-size:.65rem}.mm-ai-chat__tool-name{font-family:Roboto Mono,monospace;color:var(--mm-ai-chat-text);font-weight:600}.mm-ai-chat__tool-input{margin:.35rem 0 0;font-family:Roboto Mono,monospace;font-size:.75rem;white-space:pre-wrap;word-break:break-word;color:var(--mm-ai-chat-muted);max-height:14rem;overflow-y:auto}.mm-ai-chat__tool-result{background:var(--mm-ai-chat-card-bg);border:1px solid var(--mm-ai-chat-border);border-left:3px solid var(--mm-ai-chat-accent-tool-result);border-radius:.5rem;padding:.55rem .75rem;margin-left:1.25rem}.mm-ai-chat__tool-result--error{border-left-color:var(--mm-ai-chat-accent-error)}.mm-ai-chat__tool-result-header{display:flex;align-items:baseline;gap:.5rem;font-size:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__tool-result-header .mm-ai-chat__role{color:var(--mm-ai-chat-accent-tool-result)}.mm-ai-chat__error-badge{background:var(--mm-ai-chat-accent-error);color:#07172b;padding:0 6px;border-radius:.25rem;font-size:.6rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase}.mm-ai-chat__tool-result-body{margin:.4rem 0 0;font-family:Roboto Mono,monospace;font-size:.75rem;white-space:pre-wrap;word-break:break-word;max-height:18rem;overflow-y:auto}.mm-ai-chat__result{background:var(--mm-ai-chat-card-bg-elevated);border:1px solid var(--mm-ai-chat-border-strong);border-top:3px solid var(--mm-ai-chat-accent-result);border-radius:.5rem;padding:.7rem .85rem;display:flex;flex-direction:column;gap:.5rem}.mm-ai-chat__result--error{border-top-color:var(--mm-ai-chat-accent-error)}.mm-ai-chat__result-header{display:flex;align-items:baseline;gap:.5rem;font-size:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__result-header .mm-ai-chat__role{color:var(--mm-ai-chat-accent-result);text-transform:uppercase}.mm-ai-chat__result-text{margin:0;white-space:pre-wrap;word-break:break-word;font-style:italic;color:var(--mm-ai-chat-text)}.mm-ai-chat__result-stats{margin:0;display:grid;grid-template-columns:repeat(auto-fit,minmax(7rem,1fr));gap:.5rem;font-family:Roboto Mono,monospace;font-size:.75rem}.mm-ai-chat__result-stats div{display:flex;flex-direction:column;gap:.2rem;padding:.4rem .5rem;background:#64ceb90f;border-radius:.3rem}.mm-ai-chat__result-stats dt{font-size:.62rem;text-transform:uppercase;letter-spacing:.08em;color:var(--mm-ai-chat-muted)}.mm-ai-chat__result-stats dd{margin:0;color:var(--mm-ai-chat-text);font-variant-numeric:tabular-nums}.mm-ai-chat__system{all:unset;cursor:pointer;display:flex;align-items:center;gap:.5rem;padding:.35rem .6rem;background:#64ceb908;border:1px solid var(--mm-ai-chat-border);border-left:2px solid var(--mm-ai-chat-accent-system);border-radius:.4rem;font-size:.78rem;color:var(--mm-ai-chat-muted);transition:background-color .15s ease}.mm-ai-chat__system:hover{background:#64ceb90f}.mm-ai-chat__system:focus-visible{outline:2px solid var(--mm-ai-chat-accent-assistant);outline-offset:1px}.mm-ai-chat__system--init{border-left-color:var(--mm-ai-chat-accent-tool)}.mm-ai-chat__system--hook{border-left-color:var(--mm-ai-chat-accent-system)}.mm-ai-chat__system--rate{border-left-color:var(--mm-ai-chat-accent-rate-limit)}.mm-ai-chat__system--unknown{border-left-color:var(--mm-ai-chat-accent-error)}.mm-ai-chat__system-chevron{font-size:.7rem;width:.7rem;color:var(--mm-ai-chat-muted)}.mm-ai-chat__system-summary{flex:1;color:var(--mm-ai-chat-text);font-variant-numeric:tabular-nums}.mm-ai-chat__system-detail{margin:-.25rem 0 0 1.5rem;padding:.5rem .6rem;background:#00000040;border-radius:.35rem;font-family:Roboto Mono,monospace;font-size:.7rem;white-space:pre-wrap;word-break:break-word;max-height:22rem;overflow-y:auto;color:var(--mm-ai-chat-muted)}.mm-ai-chat__status{display:flex;align-items:baseline;gap:.5rem;padding:.25rem .5rem;font-size:.7rem;color:var(--mm-ai-chat-muted);text-transform:uppercase;letter-spacing:.06em}.mm-ai-chat__status-label{color:var(--mm-ai-chat-muted)}.mm-ai-chat__status-value{color:var(--mm-ai-chat-text);font-weight:600}\n"] }]
|
|
875
875
|
}], propDecorators: { events: [{ type: i0.Input, args: [{ isSignal: true, alias: "events", required: true }] }] } });
|
|
@@ -917,10 +917,10 @@ class AiToolCallComponent {
|
|
|
917
917
|
return String(value);
|
|
918
918
|
}
|
|
919
919
|
}
|
|
920
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
921
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
920
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiToolCallComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
921
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AiToolCallComponent, isStandalone: true, selector: "mm-ai-tool-call", inputs: { call: { classPropertyName: "call", publicName: "call", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"mm-ai-tool-call mm-ai-tool-call--{{ statusVariant() }}\">\n <button\n type=\"button\"\n class=\"mm-ai-tool-call__header\"\n (click)=\"toggleExpanded()\"\n [attr.aria-expanded]=\"expanded()\"\n >\n <span class=\"mm-ai-tool-call__chevron\">\n {{ expanded() ? '\u25BE' : '\u25B8' }}\n </span>\n <span class=\"mm-ai-tool-call__name\">{{ call().toolName }}</span>\n <span class=\"mm-ai-tool-call__status\">{{ call().status }}</span>\n @if (call().durationMs != null) {\n <span class=\"mm-ai-tool-call__duration\">\n {{ call().durationMs }}ms\n </span>\n }\n </button>\n\n @if (expanded()) {\n <div class=\"mm-ai-tool-call__body\">\n <div class=\"mm-ai-tool-call__section\">\n <h4 class=\"mm-ai-tool-call__heading\">Arguments</h4>\n <pre class=\"mm-ai-tool-call__code\">{{ argumentsJson() }}</pre>\n </div>\n\n @if (resultJson() !== null) {\n <div class=\"mm-ai-tool-call__section\">\n <h4 class=\"mm-ai-tool-call__heading\">Result</h4>\n <pre class=\"mm-ai-tool-call__code\">{{ resultJson() }}</pre>\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host{display:block;--mm-ai-tool-bg: #1f2227;--mm-ai-tool-border: #2f343b;--mm-ai-tool-text: #e6e6e6;--mm-ai-tool-muted: #9aa0a6;--mm-ai-tool-success: #2ea44f;--mm-ai-tool-failed: #d73a49;--mm-ai-tool-rejected: #d29922;--mm-ai-tool-pending: #6e7681;--mm-ai-tool-code-bg: #16191d}.mm-ai-tool-call{border:1px solid var(--mm-ai-tool-border);border-radius:.5rem;background:var(--mm-ai-tool-bg);color:var(--mm-ai-tool-text);margin:.4rem 0;overflow:hidden}.mm-ai-tool-call--succeeded{border-left:3px solid var(--mm-ai-tool-success)}.mm-ai-tool-call--failed{border-left:3px solid var(--mm-ai-tool-failed)}.mm-ai-tool-call--rejected{border-left:3px solid var(--mm-ai-tool-rejected)}.mm-ai-tool-call--pending{border-left:3px solid var(--mm-ai-tool-pending)}.mm-ai-tool-call__header{width:100%;display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;background:transparent;color:inherit;border:none;cursor:pointer;text-align:left;font-family:inherit;font-size:.9rem}.mm-ai-tool-call__header:hover{background:#ffffff0a}.mm-ai-tool-call__chevron{width:1rem;text-align:center;color:var(--mm-ai-tool-muted)}.mm-ai-tool-call__name{font-weight:600;flex:1}.mm-ai-tool-call__status,.mm-ai-tool-call__duration{font-size:.75rem;color:var(--mm-ai-tool-muted);font-variant-numeric:tabular-nums}.mm-ai-tool-call__body{border-top:1px solid var(--mm-ai-tool-border);padding:.5rem .75rem}.mm-ai-tool-call__section+.mm-ai-tool-call__section{margin-top:.75rem}.mm-ai-tool-call__heading{margin:0 0 .25rem;font-size:.75rem;text-transform:uppercase;letter-spacing:.04em;color:var(--mm-ai-tool-muted)}.mm-ai-tool-call__code{margin:0;padding:.5rem .6rem;background:var(--mm-ai-tool-code-bg);border-radius:.3rem;font-size:.8rem;overflow-x:auto;white-space:pre-wrap;word-break:break-word}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
922
922
|
}
|
|
923
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
923
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiToolCallComponent, decorators: [{
|
|
924
924
|
type: Component,
|
|
925
925
|
args: [{ selector: 'mm-ai-tool-call', imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mm-ai-tool-call mm-ai-tool-call--{{ statusVariant() }}\">\n <button\n type=\"button\"\n class=\"mm-ai-tool-call__header\"\n (click)=\"toggleExpanded()\"\n [attr.aria-expanded]=\"expanded()\"\n >\n <span class=\"mm-ai-tool-call__chevron\">\n {{ expanded() ? '\u25BE' : '\u25B8' }}\n </span>\n <span class=\"mm-ai-tool-call__name\">{{ call().toolName }}</span>\n <span class=\"mm-ai-tool-call__status\">{{ call().status }}</span>\n @if (call().durationMs != null) {\n <span class=\"mm-ai-tool-call__duration\">\n {{ call().durationMs }}ms\n </span>\n }\n </button>\n\n @if (expanded()) {\n <div class=\"mm-ai-tool-call__body\">\n <div class=\"mm-ai-tool-call__section\">\n <h4 class=\"mm-ai-tool-call__heading\">Arguments</h4>\n <pre class=\"mm-ai-tool-call__code\">{{ argumentsJson() }}</pre>\n </div>\n\n @if (resultJson() !== null) {\n <div class=\"mm-ai-tool-call__section\">\n <h4 class=\"mm-ai-tool-call__heading\">Result</h4>\n <pre class=\"mm-ai-tool-call__code\">{{ resultJson() }}</pre>\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host{display:block;--mm-ai-tool-bg: #1f2227;--mm-ai-tool-border: #2f343b;--mm-ai-tool-text: #e6e6e6;--mm-ai-tool-muted: #9aa0a6;--mm-ai-tool-success: #2ea44f;--mm-ai-tool-failed: #d73a49;--mm-ai-tool-rejected: #d29922;--mm-ai-tool-pending: #6e7681;--mm-ai-tool-code-bg: #16191d}.mm-ai-tool-call{border:1px solid var(--mm-ai-tool-border);border-radius:.5rem;background:var(--mm-ai-tool-bg);color:var(--mm-ai-tool-text);margin:.4rem 0;overflow:hidden}.mm-ai-tool-call--succeeded{border-left:3px solid var(--mm-ai-tool-success)}.mm-ai-tool-call--failed{border-left:3px solid var(--mm-ai-tool-failed)}.mm-ai-tool-call--rejected{border-left:3px solid var(--mm-ai-tool-rejected)}.mm-ai-tool-call--pending{border-left:3px solid var(--mm-ai-tool-pending)}.mm-ai-tool-call__header{width:100%;display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;background:transparent;color:inherit;border:none;cursor:pointer;text-align:left;font-family:inherit;font-size:.9rem}.mm-ai-tool-call__header:hover{background:#ffffff0a}.mm-ai-tool-call__chevron{width:1rem;text-align:center;color:var(--mm-ai-tool-muted)}.mm-ai-tool-call__name{font-weight:600;flex:1}.mm-ai-tool-call__status,.mm-ai-tool-call__duration{font-size:.75rem;color:var(--mm-ai-tool-muted);font-variant-numeric:tabular-nums}.mm-ai-tool-call__body{border-top:1px solid var(--mm-ai-tool-border);padding:.5rem .75rem}.mm-ai-tool-call__section+.mm-ai-tool-call__section{margin-top:.75rem}.mm-ai-tool-call__heading{margin:0 0 .25rem;font-size:.75rem;text-transform:uppercase;letter-spacing:.04em;color:var(--mm-ai-tool-muted)}.mm-ai-tool-call__code{margin:0;padding:.5rem .6rem;background:var(--mm-ai-tool-code-bg);border-radius:.3rem;font-size:.8rem;overflow-x:auto;white-space:pre-wrap;word-break:break-word}\n"] }]
|
|
926
926
|
}], propDecorators: { call: [{ type: i0.Input, args: [{ isSignal: true, alias: "call", required: true }] }] } });
|
|
@@ -967,10 +967,10 @@ class AiApprovalModalComponent {
|
|
|
967
967
|
const trimmed = this.comment().trim();
|
|
968
968
|
return trimmed.length > 0 ? trimmed : undefined;
|
|
969
969
|
}
|
|
970
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
971
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.
|
|
970
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiApprovalModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
971
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.17", type: AiApprovalModalComponent, isStandalone: true, selector: "mm-ai-approval-modal", inputs: { request: { classPropertyName: "request", publicName: "request", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { decide: "decide", dismiss: "dismiss" }, ngImport: i0, template: "<div class=\"mm-ai-approval\">\n <header class=\"mm-ai-approval__header\">\n <h3 class=\"mm-ai-approval__title\">Approve tool call</h3>\n <button\n type=\"button\"\n class=\"mm-ai-approval__close\"\n (click)=\"onDismiss()\"\n aria-label=\"Dismiss\"\n >\n \u00D7\n </button>\n </header>\n\n <div class=\"mm-ai-approval__meta\">\n <div><span>Tool:</span> <code>{{ request().toolName }}</code></div>\n <div><span>Reason:</span> <code>{{ request().reason }}</code></div>\n <div><span>Session:</span> <code>{{ request().sessionId }}</code></div>\n </div>\n\n <div class=\"mm-ai-approval__section\">\n <h4 class=\"mm-ai-approval__heading\">Payload</h4>\n <pre class=\"mm-ai-approval__code\">{{ prettyPayload() }}</pre>\n </div>\n\n <div class=\"mm-ai-approval__section\">\n <h4 class=\"mm-ai-approval__heading\">Comment <span>(optional)</span></h4>\n <textarea\n class=\"mm-ai-approval__comment\"\n [ngModel]=\"comment()\"\n (ngModelChange)=\"comment.set($event)\"\n rows=\"3\"\n placeholder=\"Why is this approved or rejected?\"\n ></textarea>\n </div>\n\n <footer class=\"mm-ai-approval__actions\">\n <button\n type=\"button\"\n class=\"mm-ai-approval__btn mm-ai-approval__btn--reject\"\n (click)=\"reject()\"\n >\n Reject\n </button>\n <button\n type=\"button\"\n class=\"mm-ai-approval__btn mm-ai-approval__btn--approve\"\n (click)=\"approve()\"\n >\n Approve\n </button>\n </footer>\n</div>\n", styles: [":host{display:block;--mm-ai-approval-bg: #1f2227;--mm-ai-approval-border: #2f343b;--mm-ai-approval-text: #e6e6e6;--mm-ai-approval-muted: #9aa0a6;--mm-ai-approval-approve: #2ea44f;--mm-ai-approval-reject: #d73a49;--mm-ai-approval-code-bg: #16191d}.mm-ai-approval{background:var(--mm-ai-approval-bg);color:var(--mm-ai-approval-text);border:1px solid var(--mm-ai-approval-border);border-radius:.6rem;padding:1rem 1.25rem;width:min(36rem,100%)}.mm-ai-approval__header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.75rem}.mm-ai-approval__title{margin:0;font-size:1rem}.mm-ai-approval__close{background:transparent;color:var(--mm-ai-approval-muted);border:none;font-size:1.4rem;cursor:pointer;line-height:1;padding:0 .25rem}.mm-ai-approval__close:hover{color:var(--mm-ai-approval-text)}.mm-ai-approval__meta{display:grid;gap:.25rem;font-size:.85rem;margin-bottom:.75rem}.mm-ai-approval__meta span{color:var(--mm-ai-approval-muted);margin-right:.35rem}.mm-ai-approval__meta code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.mm-ai-approval__section{margin-bottom:.75rem}.mm-ai-approval__heading{margin:0 0 .3rem;font-size:.75rem;color:var(--mm-ai-approval-muted);text-transform:uppercase;letter-spacing:.04em}.mm-ai-approval__heading span{text-transform:none;letter-spacing:0;opacity:.6}.mm-ai-approval__code{margin:0;padding:.5rem .6rem;background:var(--mm-ai-approval-code-bg);border-radius:.3rem;font-size:.8rem;overflow-x:auto;white-space:pre-wrap;word-break:break-word;max-height:14rem}.mm-ai-approval__comment{width:100%;box-sizing:border-box;padding:.5rem .6rem;background:var(--mm-ai-approval-code-bg);color:inherit;border:1px solid var(--mm-ai-approval-border);border-radius:.3rem;font-family:inherit;font-size:.85rem;resize:vertical}.mm-ai-approval__actions{display:flex;justify-content:flex-end;gap:.5rem;margin-top:.75rem}.mm-ai-approval__btn{padding:.45rem 1rem;border-radius:.3rem;font-weight:600;border:none;cursor:pointer;font-size:.85rem}.mm-ai-approval__btn--approve{background:var(--mm-ai-approval-approve);color:#fff}.mm-ai-approval__btn--reject{background:var(--mm-ai-approval-reject);color:#fff}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
972
972
|
}
|
|
973
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
973
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiApprovalModalComponent, decorators: [{
|
|
974
974
|
type: Component,
|
|
975
975
|
args: [{ selector: 'mm-ai-approval-modal', imports: [FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mm-ai-approval\">\n <header class=\"mm-ai-approval__header\">\n <h3 class=\"mm-ai-approval__title\">Approve tool call</h3>\n <button\n type=\"button\"\n class=\"mm-ai-approval__close\"\n (click)=\"onDismiss()\"\n aria-label=\"Dismiss\"\n >\n \u00D7\n </button>\n </header>\n\n <div class=\"mm-ai-approval__meta\">\n <div><span>Tool:</span> <code>{{ request().toolName }}</code></div>\n <div><span>Reason:</span> <code>{{ request().reason }}</code></div>\n <div><span>Session:</span> <code>{{ request().sessionId }}</code></div>\n </div>\n\n <div class=\"mm-ai-approval__section\">\n <h4 class=\"mm-ai-approval__heading\">Payload</h4>\n <pre class=\"mm-ai-approval__code\">{{ prettyPayload() }}</pre>\n </div>\n\n <div class=\"mm-ai-approval__section\">\n <h4 class=\"mm-ai-approval__heading\">Comment <span>(optional)</span></h4>\n <textarea\n class=\"mm-ai-approval__comment\"\n [ngModel]=\"comment()\"\n (ngModelChange)=\"comment.set($event)\"\n rows=\"3\"\n placeholder=\"Why is this approved or rejected?\"\n ></textarea>\n </div>\n\n <footer class=\"mm-ai-approval__actions\">\n <button\n type=\"button\"\n class=\"mm-ai-approval__btn mm-ai-approval__btn--reject\"\n (click)=\"reject()\"\n >\n Reject\n </button>\n <button\n type=\"button\"\n class=\"mm-ai-approval__btn mm-ai-approval__btn--approve\"\n (click)=\"approve()\"\n >\n Approve\n </button>\n </footer>\n</div>\n", styles: [":host{display:block;--mm-ai-approval-bg: #1f2227;--mm-ai-approval-border: #2f343b;--mm-ai-approval-text: #e6e6e6;--mm-ai-approval-muted: #9aa0a6;--mm-ai-approval-approve: #2ea44f;--mm-ai-approval-reject: #d73a49;--mm-ai-approval-code-bg: #16191d}.mm-ai-approval{background:var(--mm-ai-approval-bg);color:var(--mm-ai-approval-text);border:1px solid var(--mm-ai-approval-border);border-radius:.6rem;padding:1rem 1.25rem;width:min(36rem,100%)}.mm-ai-approval__header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.75rem}.mm-ai-approval__title{margin:0;font-size:1rem}.mm-ai-approval__close{background:transparent;color:var(--mm-ai-approval-muted);border:none;font-size:1.4rem;cursor:pointer;line-height:1;padding:0 .25rem}.mm-ai-approval__close:hover{color:var(--mm-ai-approval-text)}.mm-ai-approval__meta{display:grid;gap:.25rem;font-size:.85rem;margin-bottom:.75rem}.mm-ai-approval__meta span{color:var(--mm-ai-approval-muted);margin-right:.35rem}.mm-ai-approval__meta code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.mm-ai-approval__section{margin-bottom:.75rem}.mm-ai-approval__heading{margin:0 0 .3rem;font-size:.75rem;color:var(--mm-ai-approval-muted);text-transform:uppercase;letter-spacing:.04em}.mm-ai-approval__heading span{text-transform:none;letter-spacing:0;opacity:.6}.mm-ai-approval__code{margin:0;padding:.5rem .6rem;background:var(--mm-ai-approval-code-bg);border-radius:.3rem;font-size:.8rem;overflow-x:auto;white-space:pre-wrap;word-break:break-word;max-height:14rem}.mm-ai-approval__comment{width:100%;box-sizing:border-box;padding:.5rem .6rem;background:var(--mm-ai-approval-code-bg);color:inherit;border:1px solid var(--mm-ai-approval-border);border-radius:.3rem;font-family:inherit;font-size:.85rem;resize:vertical}.mm-ai-approval__actions{display:flex;justify-content:flex-end;gap:.5rem;margin-top:.75rem}.mm-ai-approval__btn{padding:.45rem 1rem;border-radius:.3rem;font-weight:600;border:none;cursor:pointer;font-size:.85rem}.mm-ai-approval__btn--approve{background:var(--mm-ai-approval-approve);color:#fff}.mm-ai-approval__btn--reject{background:var(--mm-ai-approval-reject);color:#fff}\n"] }]
|
|
976
976
|
}], propDecorators: { request: [{ type: i0.Input, args: [{ isSignal: true, alias: "request", required: true }] }], decide: [{ type: i0.Output, args: ["decide"] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }] } });
|
|
@@ -1015,10 +1015,10 @@ class AiQuotaIndicatorComponent {
|
|
|
1015
1015
|
}
|
|
1016
1016
|
return 'ok';
|
|
1017
1017
|
}
|
|
1018
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1019
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
1018
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiQuotaIndicatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1019
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AiQuotaIndicatorComponent, isStandalone: true, selector: "mm-ai-quota-indicator", inputs: { dailyConsumed: { classPropertyName: "dailyConsumed", publicName: "dailyConsumed", isSignal: true, isRequired: true, transformFunction: null }, dailyCap: { classPropertyName: "dailyCap", publicName: "dailyCap", isSignal: true, isRequired: true, transformFunction: null }, monthlyConsumed: { classPropertyName: "monthlyConsumed", publicName: "monthlyConsumed", isSignal: true, isRequired: false, transformFunction: null }, monthlyCap: { classPropertyName: "monthlyCap", publicName: "monthlyCap", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"mm-ai-quota\">\n <div class=\"mm-ai-quota__row\">\n <span class=\"mm-ai-quota__label\">Daily</span>\n <div class=\"mm-ai-quota__bar mm-ai-quota__bar--{{ dailyState() }}\">\n <div class=\"mm-ai-quota__fill\" [style.width.%]=\"dailyPercent()\"></div>\n </div>\n <span class=\"mm-ai-quota__count\">\n {{ dailyConsumed() }} / {{ dailyCap() }} ({{ dailyPercent() }}%)\n </span>\n </div>\n\n @if (monthlyPercent() !== null) {\n <div class=\"mm-ai-quota__row\">\n <span class=\"mm-ai-quota__label\">Monthly</span>\n <div class=\"mm-ai-quota__bar mm-ai-quota__bar--{{ monthlyState() }}\">\n <div class=\"mm-ai-quota__fill\" [style.width.%]=\"monthlyPercent()\"></div>\n </div>\n <span class=\"mm-ai-quota__count\">\n {{ monthlyConsumed() }} / {{ monthlyCap() }} ({{ monthlyPercent() }}%)\n </span>\n </div>\n }\n</div>\n", styles: [":host{display:block;--mm-ai-quota-track-bg: #2a2a2a;--mm-ai-quota-ok: #2ea44f;--mm-ai-quota-warning: #d29922;--mm-ai-quota-critical: #d73a49}.mm-ai-quota{display:flex;flex-direction:column;gap:.5rem;font-size:.85rem}.mm-ai-quota__row{display:grid;grid-template-columns:4.5rem 1fr auto;align-items:center;gap:.5rem}.mm-ai-quota__label{font-weight:600;opacity:.7}.mm-ai-quota__bar{height:.5rem;border-radius:9999px;background:var(--mm-ai-quota-track-bg);overflow:hidden}.mm-ai-quota__bar--ok .mm-ai-quota__fill{background:var(--mm-ai-quota-ok)}.mm-ai-quota__bar--warning .mm-ai-quota__fill{background:var(--mm-ai-quota-warning)}.mm-ai-quota__bar--critical .mm-ai-quota__fill{background:var(--mm-ai-quota-critical)}.mm-ai-quota__fill{height:100%;transition:width .2s ease}.mm-ai-quota__count{font-variant-numeric:tabular-nums;opacity:.8}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1020
1020
|
}
|
|
1021
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1021
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiQuotaIndicatorComponent, decorators: [{
|
|
1022
1022
|
type: Component,
|
|
1023
1023
|
args: [{ selector: 'mm-ai-quota-indicator', imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mm-ai-quota\">\n <div class=\"mm-ai-quota__row\">\n <span class=\"mm-ai-quota__label\">Daily</span>\n <div class=\"mm-ai-quota__bar mm-ai-quota__bar--{{ dailyState() }}\">\n <div class=\"mm-ai-quota__fill\" [style.width.%]=\"dailyPercent()\"></div>\n </div>\n <span class=\"mm-ai-quota__count\">\n {{ dailyConsumed() }} / {{ dailyCap() }} ({{ dailyPercent() }}%)\n </span>\n </div>\n\n @if (monthlyPercent() !== null) {\n <div class=\"mm-ai-quota__row\">\n <span class=\"mm-ai-quota__label\">Monthly</span>\n <div class=\"mm-ai-quota__bar mm-ai-quota__bar--{{ monthlyState() }}\">\n <div class=\"mm-ai-quota__fill\" [style.width.%]=\"monthlyPercent()\"></div>\n </div>\n <span class=\"mm-ai-quota__count\">\n {{ monthlyConsumed() }} / {{ monthlyCap() }} ({{ monthlyPercent() }}%)\n </span>\n </div>\n }\n</div>\n", styles: [":host{display:block;--mm-ai-quota-track-bg: #2a2a2a;--mm-ai-quota-ok: #2ea44f;--mm-ai-quota-warning: #d29922;--mm-ai-quota-critical: #d73a49}.mm-ai-quota{display:flex;flex-direction:column;gap:.5rem;font-size:.85rem}.mm-ai-quota__row{display:grid;grid-template-columns:4.5rem 1fr auto;align-items:center;gap:.5rem}.mm-ai-quota__label{font-weight:600;opacity:.7}.mm-ai-quota__bar{height:.5rem;border-radius:9999px;background:var(--mm-ai-quota-track-bg);overflow:hidden}.mm-ai-quota__bar--ok .mm-ai-quota__fill{background:var(--mm-ai-quota-ok)}.mm-ai-quota__bar--warning .mm-ai-quota__fill{background:var(--mm-ai-quota-warning)}.mm-ai-quota__bar--critical .mm-ai-quota__fill{background:var(--mm-ai-quota-critical)}.mm-ai-quota__fill{height:100%;transition:width .2s ease}.mm-ai-quota__count{font-variant-numeric:tabular-nums;opacity:.8}\n"] }]
|
|
1024
1024
|
}], propDecorators: { dailyConsumed: [{ type: i0.Input, args: [{ isSignal: true, alias: "dailyConsumed", required: true }] }], dailyCap: [{ type: i0.Input, args: [{ isSignal: true, alias: "dailyCap", required: true }] }], monthlyConsumed: [{ type: i0.Input, args: [{ isSignal: true, alias: "monthlyConsumed", required: false }] }], monthlyCap: [{ type: i0.Input, args: [{ isSignal: true, alias: "monthlyCap", required: false }] }] } });
|
|
@@ -1124,10 +1124,10 @@ class AiCredentialTicketIssueComponent {
|
|
|
1124
1124
|
}
|
|
1125
1125
|
return 'Failed to reach the AI Adapter — check the network.';
|
|
1126
1126
|
}
|
|
1127
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1128
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
1127
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiCredentialTicketIssueComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1128
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AiCredentialTicketIssueComponent, isStandalone: true, selector: "mm-ai-credential-ticket-issue", ngImport: i0, template: "<section class=\"mm-ai-ticket\">\n <header class=\"mm-ai-ticket__header\">\n <div class=\"mm-ai-ticket__title\">Issue credential ticket</div>\n <div class=\"mm-ai-ticket__subtitle\">\n Mint a one-time code an operator redeems on their own machine.\n </div>\n </header>\n\n @if (!issued()) {\n <form\n class=\"mm-ai-ticket__form\"\n (ngSubmit)=\"onIssue()\"\n #issueForm=\"ngForm\"\n >\n <label class=\"mm-ai-ticket__field\">\n <span class=\"mm-ai-ticket__label\">Scope</span>\n <select\n class=\"mm-ai-ticket__select\"\n [ngModel]=\"scope()\"\n (ngModelChange)=\"scope.set($event)\"\n name=\"scope\"\n [disabled]=\"issuing()\"\n >\n @for (s of scopes; track s.value) {\n <option [value]=\"s.value\">{{ s.label }}</option>\n }\n </select>\n </label>\n <p class=\"mm-ai-ticket__hint\">\n @for (s of scopes; track s.value) {\n @if (s.value === scope()) {\n {{ s.hint }}\n }\n }\n </p>\n\n <label class=\"mm-ai-ticket__field\">\n <span class=\"mm-ai-ticket__label\">TTL (minutes)</span>\n <input\n class=\"mm-ai-ticket__input\"\n type=\"number\"\n min=\"1\"\n max=\"60\"\n step=\"1\"\n [ngModel]=\"ttlMinutes()\"\n (ngModelChange)=\"ttlMinutes.set(+$event)\"\n name=\"ttlMinutes\"\n [disabled]=\"issuing()\"\n />\n </label>\n <p class=\"mm-ai-ticket__hint\">\n Default 5 minutes. Server clamps to 60 \u2014 long TTLs leak.\n </p>\n\n <div class=\"mm-ai-ticket__actions\">\n <button\n class=\"mm-ai-ticket__button mm-ai-ticket__button--primary\"\n type=\"submit\"\n [disabled]=\"issuing()\"\n >\n @if (issuing()) {\n Issuing\u2026\n } @else {\n Issue ticket\n }\n </button>\n </div>\n\n @if (error()) {\n <p class=\"mm-ai-ticket__error\">{{ error() }}</p>\n }\n </form>\n } @else {\n <div class=\"mm-ai-ticket__result\">\n <p class=\"mm-ai-ticket__result-label\">\n One-time code (copy now \u2014 not shown again):\n </p>\n <div class=\"mm-ai-ticket__code-row\">\n <code class=\"mm-ai-ticket__code\">{{ issued()!.code }}</code>\n <button\n class=\"mm-ai-ticket__button mm-ai-ticket__button--secondary\"\n type=\"button\"\n (click)=\"onCopy()\"\n >\n @if (copied()) {\n Copied\n } @else {\n Copy\n }\n </button>\n </div>\n <dl class=\"mm-ai-ticket__meta\">\n <div>\n <dt>Scope</dt>\n <dd>{{ issued()!.scope }}</dd>\n </div>\n <div>\n <dt>Expires at</dt>\n <dd>{{ issued()!.expiresAt }}</dd>\n </div>\n <div>\n <dt>Ticket rtId</dt>\n <dd><code>{{ issued()!.rtId }}</code></dd>\n </div>\n </dl>\n <div class=\"mm-ai-ticket__actions\">\n <button\n class=\"mm-ai-ticket__button\"\n type=\"button\"\n (click)=\"onClear()\"\n >\n Issue another\n </button>\n </div>\n </div>\n }\n</section>\n", styles: [":host{display:block;--mm-ai-ticket-bg: #16191d;--mm-ai-ticket-surface: #1f2227;--mm-ai-ticket-border: #2f343b;--mm-ai-ticket-text: #e6e6e6;--mm-ai-ticket-muted: #9aa0a6;--mm-ai-ticket-accent: #64ceb9;--mm-ai-ticket-error: #ec658f;--mm-ai-ticket-code-bg: #0f1115}.mm-ai-ticket{background:var(--mm-ai-ticket-bg);color:var(--mm-ai-ticket-text);border:1px solid var(--mm-ai-ticket-border);border-radius:.5rem;padding:1rem 1.25rem;display:flex;flex-direction:column;gap:.85rem;font-family:inherit}.mm-ai-ticket__header{display:flex;flex-direction:column;gap:.15rem}.mm-ai-ticket__title{font-size:1rem;font-weight:600}.mm-ai-ticket__subtitle{font-size:.8rem;color:var(--mm-ai-ticket-muted)}.mm-ai-ticket__form,.mm-ai-ticket__result{display:flex;flex-direction:column;gap:.6rem}.mm-ai-ticket__field{display:flex;flex-direction:column;gap:.25rem}.mm-ai-ticket__label{font-size:.75rem;text-transform:uppercase;letter-spacing:.05em;color:var(--mm-ai-ticket-muted)}.mm-ai-ticket__select,.mm-ai-ticket__input{background:var(--mm-ai-ticket-surface);color:var(--mm-ai-ticket-text);border:1px solid var(--mm-ai-ticket-border);border-radius:.3rem;padding:.4rem .55rem;font-family:inherit;font-size:.9rem}.mm-ai-ticket__select:focus,.mm-ai-ticket__input:focus{outline:2px solid var(--mm-ai-ticket-accent);outline-offset:1px}.mm-ai-ticket__select:disabled,.mm-ai-ticket__input:disabled{opacity:.6;cursor:not-allowed}.mm-ai-ticket__hint{margin:0;font-size:.75rem;color:var(--mm-ai-ticket-muted)}.mm-ai-ticket__actions{display:flex;gap:.5rem;margin-top:.25rem}.mm-ai-ticket__button{background:var(--mm-ai-ticket-surface);color:var(--mm-ai-ticket-text);border:1px solid var(--mm-ai-ticket-border);border-radius:.3rem;padding:.4rem .85rem;font-family:inherit;font-size:.85rem;cursor:pointer}.mm-ai-ticket__button:hover:not(:disabled){border-color:var(--mm-ai-ticket-accent)}.mm-ai-ticket__button:disabled{opacity:.5;cursor:not-allowed}.mm-ai-ticket__button--primary{background:var(--mm-ai-ticket-accent);color:#0a0c0f;border-color:var(--mm-ai-ticket-accent)}.mm-ai-ticket__error{margin:0;font-size:.8rem;color:var(--mm-ai-ticket-error)}.mm-ai-ticket__result-label{margin:0;font-size:.85rem;color:var(--mm-ai-ticket-muted)}.mm-ai-ticket__code-row{display:flex;align-items:center;gap:.5rem}.mm-ai-ticket__code{flex:1;background:var(--mm-ai-ticket-code-bg);color:var(--mm-ai-ticket-accent);padding:.5rem .75rem;border-radius:.3rem;font-family:Roboto Mono,ui-monospace,monospace;font-size:1.05rem;letter-spacing:.1em;-webkit-user-select:all;user-select:all}.mm-ai-ticket__meta{display:grid;grid-template-columns:max-content 1fr;gap:.2rem .75rem;margin:0;font-size:.8rem}.mm-ai-ticket__meta>div{display:contents}.mm-ai-ticket__meta dt{color:var(--mm-ai-ticket-muted);text-transform:uppercase;letter-spacing:.05em;font-size:.7rem}.mm-ai-ticket__meta dd{margin:0;font-family:Roboto Mono,ui-monospace,monospace}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1129
1129
|
}
|
|
1130
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1130
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiCredentialTicketIssueComponent, decorators: [{
|
|
1131
1131
|
type: Component,
|
|
1132
1132
|
args: [{ selector: 'mm-ai-credential-ticket-issue', imports: [CommonModule, FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"mm-ai-ticket\">\n <header class=\"mm-ai-ticket__header\">\n <div class=\"mm-ai-ticket__title\">Issue credential ticket</div>\n <div class=\"mm-ai-ticket__subtitle\">\n Mint a one-time code an operator redeems on their own machine.\n </div>\n </header>\n\n @if (!issued()) {\n <form\n class=\"mm-ai-ticket__form\"\n (ngSubmit)=\"onIssue()\"\n #issueForm=\"ngForm\"\n >\n <label class=\"mm-ai-ticket__field\">\n <span class=\"mm-ai-ticket__label\">Scope</span>\n <select\n class=\"mm-ai-ticket__select\"\n [ngModel]=\"scope()\"\n (ngModelChange)=\"scope.set($event)\"\n name=\"scope\"\n [disabled]=\"issuing()\"\n >\n @for (s of scopes; track s.value) {\n <option [value]=\"s.value\">{{ s.label }}</option>\n }\n </select>\n </label>\n <p class=\"mm-ai-ticket__hint\">\n @for (s of scopes; track s.value) {\n @if (s.value === scope()) {\n {{ s.hint }}\n }\n }\n </p>\n\n <label class=\"mm-ai-ticket__field\">\n <span class=\"mm-ai-ticket__label\">TTL (minutes)</span>\n <input\n class=\"mm-ai-ticket__input\"\n type=\"number\"\n min=\"1\"\n max=\"60\"\n step=\"1\"\n [ngModel]=\"ttlMinutes()\"\n (ngModelChange)=\"ttlMinutes.set(+$event)\"\n name=\"ttlMinutes\"\n [disabled]=\"issuing()\"\n />\n </label>\n <p class=\"mm-ai-ticket__hint\">\n Default 5 minutes. Server clamps to 60 \u2014 long TTLs leak.\n </p>\n\n <div class=\"mm-ai-ticket__actions\">\n <button\n class=\"mm-ai-ticket__button mm-ai-ticket__button--primary\"\n type=\"submit\"\n [disabled]=\"issuing()\"\n >\n @if (issuing()) {\n Issuing\u2026\n } @else {\n Issue ticket\n }\n </button>\n </div>\n\n @if (error()) {\n <p class=\"mm-ai-ticket__error\">{{ error() }}</p>\n }\n </form>\n } @else {\n <div class=\"mm-ai-ticket__result\">\n <p class=\"mm-ai-ticket__result-label\">\n One-time code (copy now \u2014 not shown again):\n </p>\n <div class=\"mm-ai-ticket__code-row\">\n <code class=\"mm-ai-ticket__code\">{{ issued()!.code }}</code>\n <button\n class=\"mm-ai-ticket__button mm-ai-ticket__button--secondary\"\n type=\"button\"\n (click)=\"onCopy()\"\n >\n @if (copied()) {\n Copied\n } @else {\n Copy\n }\n </button>\n </div>\n <dl class=\"mm-ai-ticket__meta\">\n <div>\n <dt>Scope</dt>\n <dd>{{ issued()!.scope }}</dd>\n </div>\n <div>\n <dt>Expires at</dt>\n <dd>{{ issued()!.expiresAt }}</dd>\n </div>\n <div>\n <dt>Ticket rtId</dt>\n <dd><code>{{ issued()!.rtId }}</code></dd>\n </div>\n </dl>\n <div class=\"mm-ai-ticket__actions\">\n <button\n class=\"mm-ai-ticket__button\"\n type=\"button\"\n (click)=\"onClear()\"\n >\n Issue another\n </button>\n </div>\n </div>\n }\n</section>\n", styles: [":host{display:block;--mm-ai-ticket-bg: #16191d;--mm-ai-ticket-surface: #1f2227;--mm-ai-ticket-border: #2f343b;--mm-ai-ticket-text: #e6e6e6;--mm-ai-ticket-muted: #9aa0a6;--mm-ai-ticket-accent: #64ceb9;--mm-ai-ticket-error: #ec658f;--mm-ai-ticket-code-bg: #0f1115}.mm-ai-ticket{background:var(--mm-ai-ticket-bg);color:var(--mm-ai-ticket-text);border:1px solid var(--mm-ai-ticket-border);border-radius:.5rem;padding:1rem 1.25rem;display:flex;flex-direction:column;gap:.85rem;font-family:inherit}.mm-ai-ticket__header{display:flex;flex-direction:column;gap:.15rem}.mm-ai-ticket__title{font-size:1rem;font-weight:600}.mm-ai-ticket__subtitle{font-size:.8rem;color:var(--mm-ai-ticket-muted)}.mm-ai-ticket__form,.mm-ai-ticket__result{display:flex;flex-direction:column;gap:.6rem}.mm-ai-ticket__field{display:flex;flex-direction:column;gap:.25rem}.mm-ai-ticket__label{font-size:.75rem;text-transform:uppercase;letter-spacing:.05em;color:var(--mm-ai-ticket-muted)}.mm-ai-ticket__select,.mm-ai-ticket__input{background:var(--mm-ai-ticket-surface);color:var(--mm-ai-ticket-text);border:1px solid var(--mm-ai-ticket-border);border-radius:.3rem;padding:.4rem .55rem;font-family:inherit;font-size:.9rem}.mm-ai-ticket__select:focus,.mm-ai-ticket__input:focus{outline:2px solid var(--mm-ai-ticket-accent);outline-offset:1px}.mm-ai-ticket__select:disabled,.mm-ai-ticket__input:disabled{opacity:.6;cursor:not-allowed}.mm-ai-ticket__hint{margin:0;font-size:.75rem;color:var(--mm-ai-ticket-muted)}.mm-ai-ticket__actions{display:flex;gap:.5rem;margin-top:.25rem}.mm-ai-ticket__button{background:var(--mm-ai-ticket-surface);color:var(--mm-ai-ticket-text);border:1px solid var(--mm-ai-ticket-border);border-radius:.3rem;padding:.4rem .85rem;font-family:inherit;font-size:.85rem;cursor:pointer}.mm-ai-ticket__button:hover:not(:disabled){border-color:var(--mm-ai-ticket-accent)}.mm-ai-ticket__button:disabled{opacity:.5;cursor:not-allowed}.mm-ai-ticket__button--primary{background:var(--mm-ai-ticket-accent);color:#0a0c0f;border-color:var(--mm-ai-ticket-accent)}.mm-ai-ticket__error{margin:0;font-size:.8rem;color:var(--mm-ai-ticket-error)}.mm-ai-ticket__result-label{margin:0;font-size:.85rem;color:var(--mm-ai-ticket-muted)}.mm-ai-ticket__code-row{display:flex;align-items:center;gap:.5rem}.mm-ai-ticket__code{flex:1;background:var(--mm-ai-ticket-code-bg);color:var(--mm-ai-ticket-accent);padding:.5rem .75rem;border-radius:.3rem;font-family:Roboto Mono,ui-monospace,monospace;font-size:1.05rem;letter-spacing:.1em;-webkit-user-select:all;user-select:all}.mm-ai-ticket__meta{display:grid;grid-template-columns:max-content 1fr;gap:.2rem .75rem;margin:0;font-size:.8rem}.mm-ai-ticket__meta>div{display:contents}.mm-ai-ticket__meta dt{color:var(--mm-ai-ticket-muted);text-transform:uppercase;letter-spacing:.05em;font-size:.7rem}.mm-ai-ticket__meta dd{margin:0;font-family:Roboto Mono,ui-monospace,monospace}\n"] }]
|
|
1133
1133
|
}] });
|
|
@@ -1233,10 +1233,10 @@ class AiNewAppDialogComponent {
|
|
|
1233
1233
|
return value !== null ? value.trim() : full;
|
|
1234
1234
|
});
|
|
1235
1235
|
}
|
|
1236
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1237
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
1236
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiNewAppDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1237
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AiNewAppDialogComponent, isStandalone: true, selector: "mm-ai-new-app-dialog", inputs: { templates: { classPropertyName: "templates", publicName: "templates", isSignal: true, isRequired: true, transformFunction: null }, tenantName: { classPropertyName: "tenantName", publicName: "tenantName", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { confirmed: "confirmed", cancelled: "cancelled" }, ngImport: i0, template: "<div class=\"mm-ai-new-app\">\n <header class=\"mm-ai-new-app__header\">\n <h3 class=\"mm-ai-new-app__title\">\n New App\n @if (tenantName(); as tenant) {\n <span class=\"mm-ai-new-app__tenant\"> \u00B7 {{ tenant }}</span>\n }\n </h3>\n <button\n type=\"button\"\n class=\"mm-ai-new-app__close\"\n (click)=\"onCancel()\"\n aria-label=\"Dismiss\"\n >\n \u00D7\n </button>\n </header>\n\n <div class=\"mm-ai-new-app__section\">\n <h4 class=\"mm-ai-new-app__heading\">Template</h4>\n <ul class=\"mm-ai-new-app__templates\" role=\"radiogroup\" aria-label=\"Template\">\n @for (template of templates(); track template.id) {\n <li>\n <label\n class=\"mm-ai-new-app__template\"\n [class.mm-ai-new-app__template--active]=\"\n selectedTemplateId() === template.id\n \"\n >\n <input\n type=\"radio\"\n name=\"mm-ai-new-app-template\"\n [value]=\"template.id\"\n [checked]=\"selectedTemplateId() === template.id\"\n (change)=\"selectedTemplateId.set(template.id)\"\n />\n <span class=\"mm-ai-new-app__template-body\">\n <strong class=\"mm-ai-new-app__template-name\">{{ template.name }}</strong>\n <span class=\"mm-ai-new-app__template-description\">\n {{ template.description }}\n </span>\n </span>\n </label>\n </li>\n }\n </ul>\n </div>\n\n @if (selectedTemplate(); as template) {\n <div class=\"mm-ai-new-app__section\">\n <h4 class=\"mm-ai-new-app__heading\">Parameters</h4>\n <div class=\"mm-ai-new-app__field\">\n <label for=\"mm-ai-new-app-project-name\">Project name</label>\n <input\n id=\"mm-ai-new-app-project-name\"\n type=\"text\"\n [ngModel]=\"projectName()\"\n (ngModelChange)=\"projectName.set($event)\"\n placeholder=\"e.g. customer-list\"\n autocomplete=\"off\"\n />\n </div>\n <div class=\"mm-ai-new-app__field\">\n <label for=\"mm-ai-new-app-primary-entity\">Primary entity (CK type)</label>\n <input\n id=\"mm-ai-new-app-primary-entity\"\n type=\"text\"\n [ngModel]=\"primaryEntity()\"\n (ngModelChange)=\"primaryEntity.set($event)\"\n placeholder=\"e.g. Customer\"\n autocomplete=\"off\"\n />\n </div>\n </div>\n\n <div class=\"mm-ai-new-app__section\">\n <h4 class=\"mm-ai-new-app__heading\">\n Generated Goal <span>(this is what Claude will see)</span>\n </h4>\n <pre class=\"mm-ai-new-app__preview\">{{\n renderedGoal() ||\n 'Fill in every field above to preview the Goal that will be sent.'\n }}</pre>\n </div>\n }\n\n <footer class=\"mm-ai-new-app__actions\">\n <button\n type=\"button\"\n class=\"mm-ai-new-app__btn mm-ai-new-app__btn--cancel\"\n (click)=\"onCancel()\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mm-ai-new-app__btn mm-ai-new-app__btn--submit\"\n [disabled]=\"!allRequiredFilled()\"\n (click)=\"onSubmit()\"\n >\n Create App Session\n </button>\n </footer>\n</div>\n", styles: [":host{display:block;--mm-ai-new-app-bg: #1f2227;--mm-ai-new-app-border: #2f343b;--mm-ai-new-app-text: #e6e6e6;--mm-ai-new-app-muted: #9aa0a6;--mm-ai-new-app-accent: #2ea44f;--mm-ai-new-app-accent-soft: rgba(46, 164, 79, .15);--mm-ai-new-app-cancel: #44494f;--mm-ai-new-app-code-bg: #16191d}.mm-ai-new-app{background:var(--mm-ai-new-app-bg);color:var(--mm-ai-new-app-text);border:1px solid var(--mm-ai-new-app-border);border-radius:.6rem;padding:1rem 1.25rem;width:min(40rem,100%)}.mm-ai-new-app__header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.75rem}.mm-ai-new-app__title{margin:0;font-size:1rem}.mm-ai-new-app__tenant{color:var(--mm-ai-new-app-muted);font-weight:400}.mm-ai-new-app__close{background:transparent;color:var(--mm-ai-new-app-muted);border:none;font-size:1.4rem;cursor:pointer;line-height:1;padding:0 .25rem}.mm-ai-new-app__close:hover{color:var(--mm-ai-new-app-text)}.mm-ai-new-app__section{margin-bottom:.75rem}.mm-ai-new-app__heading{margin:0 0 .35rem;font-size:.75rem;color:var(--mm-ai-new-app-muted);text-transform:uppercase;letter-spacing:.04em}.mm-ai-new-app__heading span{text-transform:none;letter-spacing:0;opacity:.6}.mm-ai-new-app__templates{list-style:none;margin:0;padding:0;display:grid;gap:.4rem}.mm-ai-new-app__template{display:flex;gap:.6rem;padding:.55rem .7rem;border:1px solid var(--mm-ai-new-app-border);border-radius:.35rem;cursor:pointer;align-items:flex-start}.mm-ai-new-app__template--active{background:var(--mm-ai-new-app-accent-soft);border-color:var(--mm-ai-new-app-accent)}.mm-ai-new-app__template input[type=radio]{margin-top:.2rem}.mm-ai-new-app__template-body{display:grid;gap:.2rem}.mm-ai-new-app__template-name{font-size:.9rem}.mm-ai-new-app__template-description{font-size:.8rem;color:var(--mm-ai-new-app-muted)}.mm-ai-new-app__field{display:grid;gap:.25rem;margin-bottom:.5rem;font-size:.85rem}.mm-ai-new-app__field label{color:var(--mm-ai-new-app-muted)}.mm-ai-new-app__field input{padding:.45rem .55rem;background:var(--mm-ai-new-app-code-bg);color:inherit;border:1px solid var(--mm-ai-new-app-border);border-radius:.3rem;font-family:inherit;font-size:.9rem}.mm-ai-new-app__preview{margin:0;padding:.5rem .6rem;background:var(--mm-ai-new-app-code-bg);border-radius:.3rem;font-size:.8rem;white-space:pre-wrap;word-break:break-word;max-height:12rem;overflow-y:auto}.mm-ai-new-app__actions{display:flex;justify-content:flex-end;gap:.5rem;margin-top:.75rem}.mm-ai-new-app__btn{padding:.45rem 1rem;border-radius:.3rem;font-weight:600;border:none;cursor:pointer;font-size:.85rem}.mm-ai-new-app__btn--submit{background:var(--mm-ai-new-app-accent);color:#fff}.mm-ai-new-app__btn--submit:disabled{opacity:.5;cursor:not-allowed}.mm-ai-new-app__btn--cancel{background:var(--mm-ai-new-app-cancel);color:#fff}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1238
1238
|
}
|
|
1239
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1239
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AiNewAppDialogComponent, decorators: [{
|
|
1240
1240
|
type: Component,
|
|
1241
1241
|
args: [{ selector: 'mm-ai-new-app-dialog', imports: [FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mm-ai-new-app\">\n <header class=\"mm-ai-new-app__header\">\n <h3 class=\"mm-ai-new-app__title\">\n New App\n @if (tenantName(); as tenant) {\n <span class=\"mm-ai-new-app__tenant\"> \u00B7 {{ tenant }}</span>\n }\n </h3>\n <button\n type=\"button\"\n class=\"mm-ai-new-app__close\"\n (click)=\"onCancel()\"\n aria-label=\"Dismiss\"\n >\n \u00D7\n </button>\n </header>\n\n <div class=\"mm-ai-new-app__section\">\n <h4 class=\"mm-ai-new-app__heading\">Template</h4>\n <ul class=\"mm-ai-new-app__templates\" role=\"radiogroup\" aria-label=\"Template\">\n @for (template of templates(); track template.id) {\n <li>\n <label\n class=\"mm-ai-new-app__template\"\n [class.mm-ai-new-app__template--active]=\"\n selectedTemplateId() === template.id\n \"\n >\n <input\n type=\"radio\"\n name=\"mm-ai-new-app-template\"\n [value]=\"template.id\"\n [checked]=\"selectedTemplateId() === template.id\"\n (change)=\"selectedTemplateId.set(template.id)\"\n />\n <span class=\"mm-ai-new-app__template-body\">\n <strong class=\"mm-ai-new-app__template-name\">{{ template.name }}</strong>\n <span class=\"mm-ai-new-app__template-description\">\n {{ template.description }}\n </span>\n </span>\n </label>\n </li>\n }\n </ul>\n </div>\n\n @if (selectedTemplate(); as template) {\n <div class=\"mm-ai-new-app__section\">\n <h4 class=\"mm-ai-new-app__heading\">Parameters</h4>\n <div class=\"mm-ai-new-app__field\">\n <label for=\"mm-ai-new-app-project-name\">Project name</label>\n <input\n id=\"mm-ai-new-app-project-name\"\n type=\"text\"\n [ngModel]=\"projectName()\"\n (ngModelChange)=\"projectName.set($event)\"\n placeholder=\"e.g. customer-list\"\n autocomplete=\"off\"\n />\n </div>\n <div class=\"mm-ai-new-app__field\">\n <label for=\"mm-ai-new-app-primary-entity\">Primary entity (CK type)</label>\n <input\n id=\"mm-ai-new-app-primary-entity\"\n type=\"text\"\n [ngModel]=\"primaryEntity()\"\n (ngModelChange)=\"primaryEntity.set($event)\"\n placeholder=\"e.g. Customer\"\n autocomplete=\"off\"\n />\n </div>\n </div>\n\n <div class=\"mm-ai-new-app__section\">\n <h4 class=\"mm-ai-new-app__heading\">\n Generated Goal <span>(this is what Claude will see)</span>\n </h4>\n <pre class=\"mm-ai-new-app__preview\">{{\n renderedGoal() ||\n 'Fill in every field above to preview the Goal that will be sent.'\n }}</pre>\n </div>\n }\n\n <footer class=\"mm-ai-new-app__actions\">\n <button\n type=\"button\"\n class=\"mm-ai-new-app__btn mm-ai-new-app__btn--cancel\"\n (click)=\"onCancel()\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mm-ai-new-app__btn mm-ai-new-app__btn--submit\"\n [disabled]=\"!allRequiredFilled()\"\n (click)=\"onSubmit()\"\n >\n Create App Session\n </button>\n </footer>\n</div>\n", styles: [":host{display:block;--mm-ai-new-app-bg: #1f2227;--mm-ai-new-app-border: #2f343b;--mm-ai-new-app-text: #e6e6e6;--mm-ai-new-app-muted: #9aa0a6;--mm-ai-new-app-accent: #2ea44f;--mm-ai-new-app-accent-soft: rgba(46, 164, 79, .15);--mm-ai-new-app-cancel: #44494f;--mm-ai-new-app-code-bg: #16191d}.mm-ai-new-app{background:var(--mm-ai-new-app-bg);color:var(--mm-ai-new-app-text);border:1px solid var(--mm-ai-new-app-border);border-radius:.6rem;padding:1rem 1.25rem;width:min(40rem,100%)}.mm-ai-new-app__header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.75rem}.mm-ai-new-app__title{margin:0;font-size:1rem}.mm-ai-new-app__tenant{color:var(--mm-ai-new-app-muted);font-weight:400}.mm-ai-new-app__close{background:transparent;color:var(--mm-ai-new-app-muted);border:none;font-size:1.4rem;cursor:pointer;line-height:1;padding:0 .25rem}.mm-ai-new-app__close:hover{color:var(--mm-ai-new-app-text)}.mm-ai-new-app__section{margin-bottom:.75rem}.mm-ai-new-app__heading{margin:0 0 .35rem;font-size:.75rem;color:var(--mm-ai-new-app-muted);text-transform:uppercase;letter-spacing:.04em}.mm-ai-new-app__heading span{text-transform:none;letter-spacing:0;opacity:.6}.mm-ai-new-app__templates{list-style:none;margin:0;padding:0;display:grid;gap:.4rem}.mm-ai-new-app__template{display:flex;gap:.6rem;padding:.55rem .7rem;border:1px solid var(--mm-ai-new-app-border);border-radius:.35rem;cursor:pointer;align-items:flex-start}.mm-ai-new-app__template--active{background:var(--mm-ai-new-app-accent-soft);border-color:var(--mm-ai-new-app-accent)}.mm-ai-new-app__template input[type=radio]{margin-top:.2rem}.mm-ai-new-app__template-body{display:grid;gap:.2rem}.mm-ai-new-app__template-name{font-size:.9rem}.mm-ai-new-app__template-description{font-size:.8rem;color:var(--mm-ai-new-app-muted)}.mm-ai-new-app__field{display:grid;gap:.25rem;margin-bottom:.5rem;font-size:.85rem}.mm-ai-new-app__field label{color:var(--mm-ai-new-app-muted)}.mm-ai-new-app__field input{padding:.45rem .55rem;background:var(--mm-ai-new-app-code-bg);color:inherit;border:1px solid var(--mm-ai-new-app-border);border-radius:.3rem;font-family:inherit;font-size:.9rem}.mm-ai-new-app__preview{margin:0;padding:.5rem .6rem;background:var(--mm-ai-new-app-code-bg);border-radius:.3rem;font-size:.8rem;white-space:pre-wrap;word-break:break-word;max-height:12rem;overflow-y:auto}.mm-ai-new-app__actions{display:flex;justify-content:flex-end;gap:.5rem;margin-top:.75rem}.mm-ai-new-app__btn{padding:.45rem 1rem;border-radius:.3rem;font-weight:600;border:none;cursor:pointer;font-size:.85rem}.mm-ai-new-app__btn--submit{background:var(--mm-ai-new-app-accent);color:#fff}.mm-ai-new-app__btn--submit:disabled{opacity:.5;cursor:not-allowed}.mm-ai-new-app__btn--cancel{background:var(--mm-ai-new-app-cancel);color:#fff}\n"] }]
|
|
1242
1242
|
}], propDecorators: { templates: [{ type: i0.Input, args: [{ isSignal: true, alias: "templates", required: true }] }], tenantName: [{ type: i0.Input, args: [{ isSignal: true, alias: "tenantName", required: false }] }], confirmed: [{ type: i0.Output, args: ["confirmed"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }] } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meshmakers-octo-ai-console.mjs","sources":["../../../../projects/meshmakers/octo-ai-console/src/lib/services/ai-adapter-client.service.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/services/ai-session-stream.service.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/services/ai-adapter-options.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-job-status-badge/ai-job-status-badge.component.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-job-status-badge/ai-job-status-badge.component.html","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-session-list/ai-session-list.component.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-session-list/ai-session-list.component.html","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-chat-stream/event-parser.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-chat-stream/ai-chat-stream.component.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-chat-stream/ai-chat-stream.component.html","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-tool-call/ai-tool-call.component.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-tool-call/ai-tool-call.component.html","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-approval-modal/ai-approval-modal.component.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-approval-modal/ai-approval-modal.component.html","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-quota-indicator/ai-quota-indicator.component.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-quota-indicator/ai-quota-indicator.component.html","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-credential-ticket-issue/ai-credential-ticket-issue.component.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-credential-ticket-issue/ai-credential-ticket-issue.component.html","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-new-app-dialog/ai-new-app-dialog.component.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-new-app-dialog/ai-new-app-dialog.component.html","../../../../projects/meshmakers/octo-ai-console/src/public-api.ts","../../../../projects/meshmakers/octo-ai-console/src/meshmakers-octo-ai-console.ts"],"sourcesContent":["import { HttpClient, HttpParams } from '@angular/common/http';\nimport { Injectable, inject } from '@angular/core';\nimport { Observable } from 'rxjs';\nimport { AI_ADAPTER_OPTIONS } from './ai-adapter-options';\nimport {\n AiSessionDto,\n CreateSessionRequestDto,\n CreateSessionResponseDto,\n} from '../models/ai-session';\nimport { AiSessionEventDto } from '../models/ai-session-event';\nimport { AiApprovalDecisionDto } from '../models/ai-approval';\nimport {\n IssueAiCredentialTicketRequestDto,\n IssueAiCredentialTicketResponseDto,\n} from '../models/ai-credential-ticket';\nimport {\n AiGitHubPatDto,\n RegisterAiGitHubPatRequestDto,\n} from '../models/ai-github-pat';\nimport { AiAppTemplateDto } from '../models/ai-app-template';\n\n/**\n * Typed REST client for the AI Adapter's session endpoints. Every method maps\n * 1:1 to a controller route on the C# side\n * (`src/AiServices/TenantApi/v1/Controllers/SessionsController.cs`).\n *\n * The service does NOT cache responses or hold session state — each call is a\n * pure pass-through to `HttpClient`. Components that need a derived view (e.g.\n * a session list with status counts) should compose over the returned\n * observables themselves; that keeps the client free of UI concerns and lets\n * host apps mix Apollo / GraphQL state in the same component without dueling\n * caches.\n */\n/**\n * Not `providedIn: 'root'` — `AI_ADAPTER_OPTIONS` is provided per-route in host\n * apps (the tenant id is read from the route param, which the root injector\n * doesn't see). A root-provided service would resolve through the root injector\n * and fail with NG0201 on `AI_ADAPTER_OPTIONS`. Standalone components that\n * inject this service pick up the route-level provider via the element\n * injector hierarchy.\n */\n@Injectable()\nexport class AiAdapterClientService {\n private readonly http = inject(HttpClient);\n private readonly options = inject(AI_ADAPTER_OPTIONS);\n\n /**\n * `GET /{tenantId}/v1/sessions` — list all sessions for the configured tenant.\n * Optional `status` filter narrows to one lifecycle state when present.\n */\n listSessions(status?: string): Observable<AiSessionDto[]> {\n const url = `${this.tenantBase()}/sessions`;\n let params = new HttpParams();\n if (status) {\n params = params.set('status', status);\n }\n return this.http.get<AiSessionDto[]>(url, { params });\n }\n\n /** `GET /{tenantId}/v1/sessions/{sessionId}` — single session by rtId. */\n getSession(sessionId: string): Observable<AiSessionDto> {\n return this.http.get<AiSessionDto>(\n `${this.tenantBase()}/sessions/${encodeURIComponent(sessionId)}`,\n );\n }\n\n /**\n * `POST /{tenantId}/v1/sessions` — start a new session. Returns the persisted\n * session plus the quota gate's decision so the caller can render either a\n * \"Running\" pill or a \"Queued (n)\" pill without a second round-trip.\n */\n createSession(\n request: CreateSessionRequestDto,\n ): Observable<CreateSessionResponseDto> {\n return this.http.post<CreateSessionResponseDto>(\n `${this.tenantBase()}/sessions`,\n request,\n );\n }\n\n /**\n * `POST /{tenantId}/v1/sessions/{sessionId}/cancel` — request graceful\n * cancellation; the orchestrator transitions the session to `Cancelled`\n * once the worker has acknowledged.\n */\n cancelSession(sessionId: string): Observable<AiSessionDto> {\n return this.http.post<AiSessionDto>(\n `${this.tenantBase()}/sessions/${encodeURIComponent(sessionId)}/cancel`,\n null,\n );\n }\n\n /**\n * `DELETE /{tenantId}/v1/sessions/{sessionId}` — hard-delete a terminal\n * session. The server refuses non-terminal sessions with HTTP 409 and the\n * <code>invalid_state_transition</code> error code, so callers should cancel\n * first when the session is still in flight. 204 on success; the response\n * has no body.\n */\n deleteSession(sessionId: string): Observable<void> {\n return this.http.delete<void>(\n `${this.tenantBase()}/sessions/${encodeURIComponent(sessionId)}`,\n );\n }\n\n /**\n * `POST /{tenantId}/v1/sessions/{sessionId}/messages` — append a follow-up\n * user turn to a `Completed` session. The adapter re-materialises the\n * workspace (fresh credential snapshot), recovers Claude's session id from\n * the persisted `system/init` event of the original turn, and spawns\n * <code>claude --resume <id></code> so the conversation context\n * carries over. The next turn's events stream on the same SignalR channel\n * as the prior turn — there is no separate subscription to set up.\n *\n * Server response is 202 with the updated session DTO (status flipped back\n * to `Running`); the caller can use the returned status to drive an\n * optimistic UI update. Non-Completed sessions return 409 with\n * <code>invalid_state_transition</code>; a session whose first turn died\n * before claude emitted init returns 409 with\n * <code>claude_session_id_not_captured</code>.\n */\n sendMessage(sessionId: string, text: string): Observable<AiSessionDto> {\n return this.http.post<AiSessionDto>(\n `${this.tenantBase()}/sessions/${encodeURIComponent(sessionId)}/messages`,\n { text },\n );\n }\n\n /**\n * `GET /{tenantId}/v1/sessions/{sessionId}/events?sinceSequence=N` — replay\n * persisted events for a session. UI uses this on reconnect to backfill any\n * events SignalR missed while disconnected (drives the resume-from-sequence\n * path in {@link AiSessionStreamService}).\n */\n listEvents(\n sessionId: string,\n sinceSequence?: number,\n ): Observable<AiSessionEventDto[]> {\n const url = `${this.tenantBase()}/sessions/${encodeURIComponent(sessionId)}/events`;\n let params = new HttpParams();\n if (sinceSequence != null) {\n params = params.set('sinceSequence', sinceSequence.toString());\n }\n return this.http.get<AiSessionEventDto[]>(url, { params });\n }\n\n /**\n * `POST /{tenantId}/v1/sessions/{sessionId}/approvals/{requestId}` — submit\n * the user's decision on a paused approval gate. Server fans\n * `OnApprovalDecidedAsync` to every connection on the session.\n */\n decideApproval(\n sessionId: string,\n requestId: string,\n decision: AiApprovalDecisionDto,\n ): Observable<void> {\n return this.http.post<void>(\n `${this.tenantBase()}/sessions/${encodeURIComponent(sessionId)}` +\n `/approvals/${encodeURIComponent(requestId)}`,\n decision,\n );\n }\n\n /**\n * `POST /{tenantId}/v1/credentials/tickets` — mint a one-time credential-\n * registration ticket (#4133). The admin sees the plaintext code once in\n * the response; the server stores only the SHA-256 hash. The matching\n * redemption call (`POST /v1/credentials/tickets/redeem`) is anonymous and\n * NOT in this client because the redeemer is by design a different person\n * on a different machine — they use the bastion CLI, not the Studio.\n */\n issueCredentialTicket(\n request: IssueAiCredentialTicketRequestDto,\n ): Observable<IssueAiCredentialTicketResponseDto> {\n return this.http.post<IssueAiCredentialTicketResponseDto>(\n `${this.tenantBase()}/credentials/tickets`,\n request,\n );\n }\n\n /**\n * `POST /{tenantId}/v1/credentials/github-pat` — register a GitHub Personal\n * Access Token (#4124). The adapter validates the token against GitHub's\n * `/user` endpoint before encryption; a refused token surfaces as HTTP 422\n * with `error: 'github_refused_token'`. The response is intentionally minimal\n * (`{ rtId }`); the host re-fetches the list so the rendering path stays\n * shared with refresh.\n */\n registerGitHubPat(\n request: RegisterAiGitHubPatRequestDto,\n ): Observable<{ rtId: string }> {\n return this.http.post<{ rtId: string }>(\n `${this.tenantBase()}/credentials/github-pat`,\n request,\n );\n }\n\n /**\n * `GET /{tenantId}/v1/credentials/github-pat` — list registered PATs with a\n * masked tail + GitHub login. Plaintext never crosses the wire.\n */\n listGitHubPats(): Observable<AiGitHubPatDto[]> {\n return this.http.get<AiGitHubPatDto[]>(\n `${this.tenantBase()}/credentials/github-pat`,\n );\n }\n\n /**\n * `DELETE /{tenantId}/v1/credentials/github-pat/{rtId}` — revoke a PAT. The\n * ciphertext is deleted, not soft-tombstoned. Returns void on 204.\n */\n deleteGitHubPat(rtId: string): Observable<void> {\n return this.http.delete<void>(\n `${this.tenantBase()}/credentials/github-pat/${encodeURIComponent(rtId)}`,\n );\n }\n\n /**\n * `GET /{tenantId}/v1/app-templates` — list the Custom-App templates the\n * adapter exposes for the wizard (#4145). Phase-1 returns the same three\n * static entries for every tenant; the wire shape is stable so per-tenant\n * overrides can ride it without an SPA bump.\n */\n getAppTemplates(): Observable<AiAppTemplateDto[]> {\n return this.http.get<AiAppTemplateDto[]>(\n `${this.tenantBase()}/app-templates`,\n );\n }\n\n private tenantBase(): string {\n return `${this.options.baseUrl}/${encodeURIComponent(this.options.tenantId)}/v1`;\n }\n}\n","import { Injectable, inject } from '@angular/core';\nimport { Observable, ReplaySubject, Subject } from 'rxjs';\nimport {\n AI_ADAPTER_OPTIONS,\n AI_SESSION_STREAM_CONNECTION_FACTORY,\n AiHubConnectionLike,\n} from './ai-adapter-options';\nimport { AiAdapterClientService } from './ai-adapter-client.service';\nimport {\n AiSessionEventDto,\n AiSessionStatusChangedDto,\n} from '../models/ai-session-event';\nimport {\n AiApprovalDecidedDto,\n AiApprovalRequestedDto,\n} from '../models/ai-approval';\nimport { AiQuotaWarningDto } from '../models/ai-quota';\n\n/**\n * One open SignalR connection's worth of streams. Sessions get one\n * `SessionStream` per `streamSession()` call; closing the returned subscription\n * tears the connection down.\n */\nexport interface AiSessionStream {\n /** Stream-json lines plus orchestrator markers, ordered by `sequence`. */\n readonly events$: Observable<AiSessionEventDto>;\n /** Lifecycle transitions for the session. */\n readonly statusChanges$: Observable<AiSessionStatusChangedDto>;\n /** Approval gate fired — UI opens the modal. */\n readonly approvalsRequested$: Observable<AiApprovalRequestedDto>;\n /** Approval resolved — UI dismisses the modal in every tab. */\n readonly approvalsDecided$: Observable<AiApprovalDecidedDto>;\n /** Tenant-wide quota threshold crossed — UI renders a banner. */\n readonly quotaWarnings$: Observable<AiQuotaWarningDto>;\n\n /** Tear down the underlying SignalR connection and complete all observables. */\n disconnect(): void;\n}\n\n/**\n * Internal wrapper around the SignalR connection. The library does not pin a\n * SignalR version — host applications inject a `HubConnectionBuilder` they\n * already use, which keeps the library agnostic of the\n * `@microsoft/signalr` major version line (six and seven have diverged on the\n * `withAutomaticReconnect()` defaults).\n *\n * For now the connection is constructed deferred — `streamSession` builds it on\n * demand so the host can defer the SignalR JS bundle behind a route lazy-load.\n * Phase-1 ships a stub backed by `ReplaySubject` so demo apps can drive\n * components without a live hub; the real connection wiring lives behind a\n * `connectionFactory` provider the host overrides.\n */\n/**\n * Not `providedIn: 'root'` — see <see cref=\"AiAdapterClientService\"/> for the\n * rationale: `AI_ADAPTER_OPTIONS` is a per-route token, the root injector\n * cannot resolve it. Element-level injection picks up the route-level provider.\n */\n@Injectable()\nexport class AiSessionStreamService {\n private readonly options = inject(AI_ADAPTER_OPTIONS);\n private readonly client = inject(AiAdapterClientService);\n private readonly connectionFactory = inject(\n AI_SESSION_STREAM_CONNECTION_FACTORY,\n { optional: true },\n );\n\n /**\n * Open a streaming session. Returns the per-stream subjects and a\n * `disconnect()` closer. When the host has provided an\n * {@link AI_SESSION_STREAM_CONNECTION_FACTORY}, a live SignalR connection is\n * built against {@link hubUrl} and the five hub callbacks\n * (`OnSessionEventAsync`, `OnSessionStatusChangedAsync`,\n * `OnApprovalRequestedAsync`, `OnApprovalDecidedAsync`, `OnQuotaWarningAsync`)\n * are routed into the matching subject. Without a factory, the service\n * degrades to REST-only backfill — the original Phase-1 stub contract.\n *\n * Tracking the highest sequence we've emitted lets the host reconnect after a\n * network blip by passing `sinceSequence` to {@link replay} (the SignalR\n * `onreconnected` hook fires that with the highest seen sequence).\n */\n streamSession(sessionId: string): AiSessionStream {\n const events$ = new ReplaySubject<AiSessionEventDto>(256);\n const statusChanges$ = new Subject<AiSessionStatusChangedDto>();\n const approvalsRequested$ = new Subject<AiApprovalRequestedDto>();\n const approvalsDecided$ = new Subject<AiApprovalDecidedDto>();\n const quotaWarnings$ = new Subject<AiQuotaWarningDto>();\n\n // Backfill any persisted events the hub-connect process would have missed\n // — REST returns the full history up to \"now\", and the live channel takes\n // it from there. The combination is at-least-once with deterministic\n // ordering courtesy of the persisted `sequence` field.\n let highestSequence = 0;\n const backfill = this.client.listEvents(sessionId).subscribe({\n next: (events) => {\n for (const event of events) {\n highestSequence = Math.max(highestSequence, event.sequence);\n events$.next(event);\n }\n },\n });\n\n let connection: AiHubConnectionLike | null = null;\n if (this.connectionFactory) {\n connection = this.connectionFactory(this.hubUrl);\n }\n\n if (connection) {\n const conn = connection;\n conn.on('OnSessionEventAsync', (payload) => {\n const event = payload as AiSessionEventDto;\n highestSequence = Math.max(highestSequence, event.sequence ?? 0);\n events$.next(event);\n });\n conn.on('OnSessionStatusChangedAsync', (payload) => {\n statusChanges$.next(payload as AiSessionStatusChangedDto);\n });\n conn.on('OnApprovalRequestedAsync', (payload) => {\n approvalsRequested$.next(payload as AiApprovalRequestedDto);\n });\n conn.on('OnApprovalDecidedAsync', (payload) => {\n approvalsDecided$.next(payload as AiApprovalDecidedDto);\n });\n conn.on('OnQuotaWarningAsync', (payload) => {\n quotaWarnings$.next(payload as AiQuotaWarningDto);\n });\n conn.onreconnected(() => {\n // Pull anything we missed during the disconnect window so the\n // events$ stream stays gap-free without the caller having to do\n // their own bookkeeping.\n this.replay(sessionId, highestSequence, events$);\n });\n\n // Start the connection, then ask the hub to scope this connection's\n // group membership to the session. Failures are intentionally swallowed\n // — backfill stays valid and the UI degrades to REST-only instead of\n // crashing.\n conn\n .start()\n .then(() => conn.invoke('SubscribeToSessionAsync', sessionId))\n .catch(() => {\n /* hub start / subscribe failed — REST backfill still serves the UI */\n });\n }\n\n return {\n events$: events$.asObservable(),\n statusChanges$: statusChanges$.asObservable(),\n approvalsRequested$: approvalsRequested$.asObservable(),\n approvalsDecided$: approvalsDecided$.asObservable(),\n quotaWarnings$: quotaWarnings$.asObservable(),\n disconnect: () => {\n backfill.unsubscribe();\n if (connection) {\n // Best-effort — `stop()` may reject during teardown; the subjects\n // get completed regardless so consumers see clean termination.\n connection.stop().catch(() => undefined);\n connection = null;\n }\n events$.complete();\n statusChanges$.complete();\n approvalsRequested$.complete();\n approvalsDecided$.complete();\n quotaWarnings$.complete();\n },\n };\n }\n\n /**\n * Backfill missing events from a known sequence number and push them into\n * the host's existing stream. Used by a SignalR `onreconnected` hook the\n * host wires up.\n */\n replay(\n sessionId: string,\n sinceSequence: number,\n sink: Subject<AiSessionEventDto> | ReplaySubject<AiSessionEventDto>,\n ): void {\n this.client.listEvents(sessionId, sinceSequence).subscribe({\n next: (events) => events.forEach((event) => sink.next(event)),\n });\n }\n\n /**\n * Tenant-scoped hub URL. Composition matches the AI Adapter's hub map —\n * `app.MapHub<AiHub>(\"/{tenantId:tenantId}/aiHub\")` — so the host's\n * `hubPath` is appended after the tenant segment. Host apps that mount the\n * hub at a different relative path override `hubPath` via\n * `AI_ADAPTER_OPTIONS`.\n */\n get hubUrl(): string {\n const trimmed = this.options.hubPath.startsWith('/')\n ? this.options.hubPath\n : `/${this.options.hubPath}`;\n return `${this.options.baseUrl}/${this.options.tenantId}${trimmed}`;\n }\n}\n","import {\n EnvironmentProviders,\n InjectionToken,\n makeEnvironmentProviders,\n} from '@angular/core';\nimport { AiAdapterClientService } from './ai-adapter-client.service';\nimport { AiSessionStreamService } from './ai-session-stream.service';\n\n/**\n * Configuration the host application supplies via {@link provideOctoAiConsole}.\n * Every field is required so the library never falls back to a hard-coded URL or\n * tenant id — host apps that wire the library through their own DI container can\n * surface placeholders explicitly instead.\n */\nexport interface AiAdapterOptions {\n /**\n * Absolute base URL of the AI Adapter REST API, e.g. `https://ai.acme.cloud`.\n * Must NOT include a trailing slash. The library appends\n * `/{tenantId}/v1/sessions...` itself.\n */\n readonly baseUrl: string;\n\n /**\n * Tenant whose sessions the host wants to surface. The library renders the\n * tenant id into every REST URL. Host apps that have multiple tenants must\n * compose the provider with the tenant id from their routing context.\n */\n readonly tenantId: string;\n\n /**\n * SignalR hub path, relative to the base URL, e.g. `/hubs/ai`. Used by\n * `AiSessionStreamService` to connect when a host calls\n * `streamSession(sessionId)`.\n */\n readonly hubPath: string;\n}\n\n/**\n * DI token the services in this library read at construction time.\n * Provided by {@link provideOctoAiConsole}.\n */\nexport const AI_ADAPTER_OPTIONS = new InjectionToken<AiAdapterOptions>(\n 'mm-octo-ai-console.adapter-options',\n);\n\n/**\n * Minimal subset of the `@microsoft/signalr` `HubConnection` surface the library\n * exercises. The interface stays in the public API so host apps can supply\n * either the real SignalR connection (Refinery Studio, bastion CLI), a test\n * mock, or no factory at all — in which case {@link AiSessionStreamService}\n * falls back to REST-only backfill (Phase-1 stub behaviour). Keeping the type\n * structural lets the library stay agnostic of the `@microsoft/signalr` major\n * version line the host has installed.\n */\nexport interface AiHubConnectionLike {\n on(methodName: string, handler: (payload: unknown) => void): void;\n start(): Promise<void>;\n stop(): Promise<void>;\n invoke<T = unknown>(methodName: string, ...args: unknown[]): Promise<T>;\n onreconnected(handler: (connectionId?: string) => void): void;\n}\n\n/**\n * Builds a hub connection against the resolved URL. Return null when streaming\n * is not wired in this deployment yet — the stream service then quietly skips\n * the live channel and behaves exactly like the Phase-1 stub.\n */\nexport type AiSessionStreamConnectionFactory =\n (hubUrl: string) => AiHubConnectionLike | null;\n\n/**\n * Optional DI token a host application provides to enable the live SignalR\n * channel on {@link AiSessionStreamService}. When absent, the service degrades\n * to REST-only backfill.\n */\nexport const AI_SESSION_STREAM_CONNECTION_FACTORY =\n new InjectionToken<AiSessionStreamConnectionFactory>(\n 'mm-octo-ai-console.session-stream-connection-factory',\n );\n\n/**\n * Composable provider that surfaces the library's adapter configuration to the\n * Angular DI container. Host applications call this once in `app.config.ts`:\n *\n * ```ts\n * providers: [\n * provideOctoAiConsole({\n * baseUrl: 'https://ai.acme.cloud',\n * tenantId: 'acme',\n * hubPath: '/hubs/ai',\n * }),\n * ]\n * ```\n */\nexport function provideOctoAiConsole(\n options: AiAdapterOptions,\n): EnvironmentProviders {\n // Provide AiAdapterClientService + AiSessionStreamService alongside the\n // options token. Both services drop `providedIn: 'root'` (6d0e7daf) because\n // the options they read are route-scoped — a host with multiple tenants\n // composes the provider with the right tenant id per route, and root-\n // singleton services would freeze the first route's options. Bundling the\n // services into the same provider chain means a host's `providers: [\n // provideOctoAiConsole({...}) ]` is enough; no manual listing required.\n return makeEnvironmentProviders([\n { provide: AI_ADAPTER_OPTIONS, useValue: options },\n AiAdapterClientService,\n AiSessionStreamService,\n ]);\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { AiSessionStatus } from '../../models/ai-session';\n\n/**\n * Status pill that mirrors the eight values `AiSessionStatus` carries on the\n * wire. The component is purely visual — it takes a status string in and emits\n * a CSS class + display label that the host's stylesheet can theme via the\n * `--mm-ai-status-*` custom properties.\n */\n@Component({\n selector: 'mm-ai-job-status-badge',\n imports: [],\n templateUrl: './ai-job-status-badge.component.html',\n styleUrl: './ai-job-status-badge.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AiJobStatusBadgeComponent {\n readonly status = input.required<AiSessionStatus>();\n\n protected readonly variant = computed(() => {\n switch (this.status()) {\n case 'Running':\n return 'running';\n case 'Queued':\n return 'queued';\n case 'Paused':\n return 'paused';\n case 'Completed':\n return 'completed';\n case 'Failed':\n case 'Cancelled':\n return 'failed';\n case 'QuotaBlocked':\n case 'RateLimited':\n return 'blocked';\n default:\n return 'queued';\n }\n });\n}\n","<span class=\"mm-ai-status-badge mm-ai-status-badge--{{ variant() }}\">\n {{ status() }}\n</span>\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n input,\n output,\n} from '@angular/core';\nimport { AiSessionDto, AiSessionStatus } from '../../models/ai-session';\nimport { AiJobStatusBadgeComponent } from '../ai-job-status-badge/ai-job-status-badge.component';\n\n/**\n * List view of an AI Adapter tenant's sessions. The host owns data fetching —\n * the component takes the array as an input signal and emits the rtId on\n * row click. Keeping it data-source-agnostic lets refinery wire its own\n * Apollo cache while a bastion CLI could pass static fixtures in a Storybook.\n *\n * Cancel and Delete are surfaced as inline per-row actions and emitted as\n * separate outputs — the host owns the confirmation dialog and the actual\n * REST call. The component only knows which actions are valid for which\n * status, so a Running session never offers Delete and a Completed session\n * never offers Cancel.\n */\n@Component({\n selector: 'mm-ai-session-list',\n imports: [AiJobStatusBadgeComponent],\n templateUrl: './ai-session-list.component.html',\n styleUrl: './ai-session-list.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AiSessionListComponent {\n readonly sessions = input.required<AiSessionDto[]>();\n readonly selectedSessionId = input<string | null>(null);\n\n readonly sessionSelected = output<string>();\n readonly cancelRequested = output<string>();\n readonly deleteRequested = output<string>();\n\n protected readonly orderedSessions = computed(() =>\n [...this.sessions()].sort((a, b) =>\n b.startedAt.localeCompare(a.startedAt),\n ),\n );\n\n /**\n * Terminal status set, kept in sync with the C# `AiAgentSessionService.IsTerminal`\n * predicate. Delete is gated to this set; Cancel is gated to its complement.\n * If the wire enum grows a new terminal kind, both predicates must be\n * updated here AND on the server.\n */\n private static readonly TERMINAL_STATUSES: ReadonlySet<AiSessionStatus> =\n new Set<AiSessionStatus>(['Completed', 'Failed', 'Cancelled']);\n\n protected canCancel(status: AiSessionStatus): boolean {\n return !AiSessionListComponent.TERMINAL_STATUSES.has(status);\n }\n\n protected canDelete(status: AiSessionStatus): boolean {\n return AiSessionListComponent.TERMINAL_STATUSES.has(status);\n }\n\n protected onSelect(sessionRtId: string): void {\n this.sessionSelected.emit(sessionRtId);\n }\n\n protected onCancel(sessionRtId: string, event: MouseEvent): void {\n // Without stopPropagation the row click also fires and selects this\n // session, which then redirects focus and tears the inline-action button\n // out from under the user mid-confirmation.\n event.stopPropagation();\n this.cancelRequested.emit(sessionRtId);\n }\n\n protected onDelete(sessionRtId: string, event: MouseEvent): void {\n event.stopPropagation();\n this.deleteRequested.emit(sessionRtId);\n }\n\n protected age(startedAt: string): string {\n const started = Date.parse(startedAt);\n if (Number.isNaN(started)) {\n return '';\n }\n const now = Date.now();\n const seconds = Math.floor((now - started) / 1000);\n if (seconds < 60) {\n return `${seconds}s ago`;\n }\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) {\n return `${minutes}m ago`;\n }\n const hours = Math.floor(minutes / 60);\n if (hours < 24) {\n return `${hours}h ago`;\n }\n const days = Math.floor(hours / 24);\n return `${days}d ago`;\n }\n}\n","<ul class=\"mm-ai-session-list\">\n @for (session of orderedSessions(); track session.sessionRtId) {\n <li\n class=\"mm-ai-session-list__row\"\n [class.mm-ai-session-list__row--selected]=\"\n session.sessionRtId === selectedSessionId()\n \"\n >\n <button\n type=\"button\"\n class=\"mm-ai-session-list__button\"\n (click)=\"onSelect(session.sessionRtId)\"\n >\n <!--\n Row layout: goal + status badge on top, meta + actions on the\n bottom. The two-line shape lets a narrow sidebar (Refinery\n Studio caps the column at 18rem) breathe — the previous\n horizontal cram had the goal text overlap the action buttons\n on every row.\n -->\n <div class=\"mm-ai-session-list__top\">\n <span class=\"mm-ai-session-list__goal\">{{ session.goalSummary }}</span>\n <mm-ai-job-status-badge [status]=\"session.status\" />\n </div>\n <div class=\"mm-ai-session-list__bottom\">\n <span class=\"mm-ai-session-list__meta\">\n {{ age(session.startedAt) }}\n <span class=\"mm-ai-session-list__dot\">·</span>\n {{ session.tokensConsumed }} tok\n </span>\n @if (session.previewUrl) {\n <!--\n #4136 — preview-deploy URL the server surfaces when the\n worker pod's nginx sidecar has a build to serve. Opens in\n a new tab so the sidebar scroll position survives.\n stopPropagation prevents the row click from also firing.\n -->\n <a\n class=\"mm-ai-session-list__preview\"\n [href]=\"session.previewUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n (click)=\"$event.stopPropagation()\"\n >\n Preview\n </a>\n }\n <span class=\"mm-ai-session-list__spacer\"></span>\n <span class=\"mm-ai-session-list__actions\">\n @if (canCancel(session.status)) {\n <button\n type=\"button\"\n class=\"mm-ai-session-list__action mm-ai-session-list__action--cancel\"\n [attr.aria-label]=\"'Cancel session ' + session.goalSummary\"\n (click)=\"onCancel(session.sessionRtId, $event)\"\n >\n Cancel\n </button>\n }\n @if (canDelete(session.status)) {\n <button\n type=\"button\"\n class=\"mm-ai-session-list__action mm-ai-session-list__action--delete\"\n [attr.aria-label]=\"'Delete session ' + session.goalSummary\"\n (click)=\"onDelete(session.sessionRtId, $event)\"\n >\n Delete\n </button>\n }\n </span>\n </div>\n </button>\n </li>\n } @empty {\n <li class=\"mm-ai-session-list__empty\">No sessions yet.</li>\n }\n</ul>\n","import { AiSessionEventDto } from '../../models/ai-session-event';\n\n/**\n * Token-usage summary extracted from an `assistant` or `result` payload.\n * Fields beyond `input` / `output` are optional because the upstream worker\n * (claude-code stream-json) sometimes omits them on rate-limited or\n * cache-only turns.\n */\nexport interface ParsedTokenUsage {\n readonly input: number;\n readonly output: number;\n readonly cacheRead?: number;\n readonly cacheCreation?: number;\n}\n\n/**\n * A single content fragment inside an assistant event. The worker emits a\n * mix of `text` and `tool_use` parts per message — sometimes one of each in\n * the same payload — and the renderer needs to walk them in order to keep\n * the conversational flow intact (text → \"I'll run X\" → tool_use card).\n */\nexport type ParsedAssistantPart =\n | { readonly type: 'text'; readonly text: string }\n | {\n readonly type: 'tool_use';\n readonly id: string;\n readonly toolName: string;\n readonly input: unknown;\n };\n\n/**\n * Discriminated union the chat-stream renders on. The component switches over\n * `kind` and pulls only the fields it needs per branch — no `any` walks of\n * the original payload at render time.\n *\n * `raw` is kept on every variant so a debug-view (\"show me the original\n * stream-json\") can surface it on demand without re-parsing.\n */\nexport type ParsedView =\n | {\n readonly kind: 'system';\n readonly subKind: 'init' | 'hook' | 'rate-limit' | 'other';\n readonly summary: string;\n readonly raw: unknown;\n }\n | {\n readonly kind: 'assistant';\n readonly parts: readonly ParsedAssistantPart[];\n readonly model: string | null;\n readonly usage: ParsedTokenUsage | null;\n readonly raw: unknown;\n }\n | {\n readonly kind: 'tool-result';\n readonly toolUseId: string;\n readonly content: string;\n readonly isError: boolean;\n readonly raw: unknown;\n }\n | {\n readonly kind: 'result';\n readonly text: string;\n readonly durationMs: number;\n readonly usage: ParsedTokenUsage | null;\n readonly costUsd: number | null;\n readonly reason: string;\n readonly isError: boolean;\n readonly raw: unknown;\n }\n | {\n readonly kind: 'status-change';\n readonly status: string;\n readonly raw: unknown;\n }\n | {\n readonly kind: 'unknown';\n readonly summary: string;\n readonly raw: unknown;\n };\n\n/**\n * Result of one parse step — wraps the source event so the renderer can still\n * carry `sequence` / `at` / `actorRef` through to the template.\n */\nexport interface ParsedEvent {\n readonly event: AiSessionEventDto;\n readonly view: ParsedView;\n}\n\n/**\n * Parse one persisted session event into a `ParsedView`. Falls back to\n * `unknown` (never throws) when the payload is malformed — we'd rather show\n * \"unrecognised event\" than crash the whole transcript.\n */\nexport function parseSessionEvent(event: AiSessionEventDto): ParsedEvent {\n const payload = safeJsonParse(event.payload);\n if (payload === parseFailure) {\n return {\n event,\n view: {\n kind: 'unknown',\n summary: 'Malformed event payload',\n raw: event.payload,\n },\n };\n }\n\n if (event.kind === 'StatusChange' && isRecord(payload)) {\n // The orchestrator emits a StatusChange wrapper around any non-content\n // worker event — system/init, hooks, rate-limit, etc. Demote based on the\n // wrapped `type` so the renderer can collapse the noise.\n if (isClaudeStreamFrame(payload)) {\n return { event, view: viewFromClaudeFrame(payload) };\n }\n const status = readString(payload, 'newStatus') ?? readString(payload, 'status');\n if (status) {\n return { event, view: { kind: 'status-change', status, raw: payload } };\n }\n }\n\n if (event.kind === 'Hook' && isRecord(payload)) {\n return { event, view: viewSystemHook(payload) };\n }\n\n if (event.kind === 'Message' && isRecord(payload)) {\n if (isClaudeStreamFrame(payload)) {\n return { event, view: viewFromClaudeFrame(payload) };\n }\n // Bare text Message — promote to a single-part assistant bubble so the\n // visual treatment is consistent.\n const text = readString(payload, 'text');\n if (text) {\n return {\n event,\n view: {\n kind: 'assistant',\n parts: [{ type: 'text', text }],\n model: null,\n usage: null,\n raw: payload,\n },\n };\n }\n }\n\n if (event.kind === 'ToolCall' && isRecord(payload)) {\n return {\n event,\n view: {\n kind: 'assistant',\n parts: [\n {\n type: 'tool_use',\n id: readString(payload, 'callId') ?? '',\n toolName: readString(payload, 'toolName') ?? 'tool',\n input: payload['arguments'] ?? null,\n },\n ],\n model: null,\n usage: null,\n raw: payload,\n },\n };\n }\n\n if (event.kind === 'ToolResult' && isRecord(payload)) {\n return {\n event,\n view: {\n kind: 'tool-result',\n toolUseId: readString(payload, 'callId') ?? readString(payload, 'toolUseId') ?? '',\n content: stringifyToolContent(payload['result'] ?? payload['content']),\n isError: readBoolean(payload, 'isError') ?? false,\n raw: payload,\n },\n };\n }\n\n if (event.kind === 'Error') {\n return {\n event,\n view: {\n kind: 'unknown',\n summary: typeof payload === 'string' ? payload : 'Error event',\n raw: payload,\n },\n };\n }\n\n return {\n event,\n view: { kind: 'unknown', summary: `Unhandled kind: ${event.kind}`, raw: payload },\n };\n}\n\n/**\n * Sort parsed events by sequence so out-of-order arrival (the REST backfill +\n * live SignalR union) renders deterministically. Kept here so the component\n * doesn't redo the sort logic every render cycle.\n */\nexport function parseAndOrder(events: readonly AiSessionEventDto[]): ParsedEvent[] {\n return [...events]\n .sort((a, b) => a.sequence - b.sequence)\n .map(parseSessionEvent);\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Internals\n// ────────────────────────────────────────────────────────────────────────────\n\nconst parseFailure = Symbol('parseFailure');\n\nfunction safeJsonParse(raw: string): unknown | typeof parseFailure {\n try {\n return JSON.parse(raw);\n } catch {\n return parseFailure;\n }\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nfunction readString(rec: Record<string, unknown>, key: string): string | null {\n const v = rec[key];\n return typeof v === 'string' && v.length > 0 ? v : null;\n}\n\nfunction readNumber(rec: Record<string, unknown>, key: string): number | null {\n const v = rec[key];\n return typeof v === 'number' && Number.isFinite(v) ? v : null;\n}\n\nfunction readBoolean(rec: Record<string, unknown>, key: string): boolean | null {\n const v = rec[key];\n return typeof v === 'boolean' ? v : null;\n}\n\n/**\n * Claude Code's stream-json shapes a frame as `{ type, ... }` with a small\n * vocabulary: `system`, `assistant`, `user`, `result`, `rate_limit_event`.\n * We branch on `type` (+ optional `subtype` for `system`) and rely on every\n * other claim being optional — the renderer must not crash on a frame we've\n * never seen.\n */\nfunction isClaudeStreamFrame(rec: Record<string, unknown>): boolean {\n return typeof rec['type'] === 'string';\n}\n\nfunction viewFromClaudeFrame(rec: Record<string, unknown>): ParsedView {\n const type = rec['type'];\n\n if (type === 'system') {\n const subtype = readString(rec, 'subtype');\n if (subtype === 'init') {\n return {\n kind: 'system',\n subKind: 'init',\n summary: `Worker initialised — ${readString(rec, 'model') ?? 'model'}`,\n raw: rec,\n };\n }\n if (subtype === 'hook_started' || subtype === 'hook_response') {\n const hookName = readString(rec, 'hook_name') ?? 'hook';\n return {\n kind: 'system',\n subKind: 'hook',\n summary:\n subtype === 'hook_started'\n ? `Hook started — ${hookName}`\n : `Hook completed — ${hookName}`,\n raw: rec,\n };\n }\n return {\n kind: 'system',\n subKind: 'other',\n summary: `System event${subtype ? ' — ' + subtype : ''}`,\n raw: rec,\n };\n }\n\n if (type === 'rate_limit_event') {\n const info = rec['rate_limit_info'];\n let summary = 'Rate-limit event';\n if (isRecord(info)) {\n const status = readString(info, 'status');\n const rateLimitType = readString(info, 'rateLimitType');\n if (status && rateLimitType) {\n summary = `Rate-limit ${status} (${rateLimitType})`;\n }\n }\n return { kind: 'system', subKind: 'rate-limit', summary, raw: rec };\n }\n\n if (type === 'assistant') {\n const message = rec['message'];\n if (isRecord(message)) {\n const parts = extractAssistantParts(message['content']);\n const model = readString(message, 'model');\n const usage = isRecord(message['usage']) ? extractUsage(message['usage']) : null;\n return {\n kind: 'assistant',\n parts,\n model,\n usage,\n raw: rec,\n };\n }\n }\n\n if (type === 'user') {\n // claude wraps tool_result inside a `user` frame's content. Extract the\n // first tool_result we find; if there isn't one, fall through to unknown.\n const message = rec['message'];\n if (isRecord(message) && Array.isArray(message['content'])) {\n for (const part of message['content']) {\n if (isRecord(part) && part['type'] === 'tool_result') {\n const rawContent = part['content'];\n return {\n kind: 'tool-result',\n toolUseId: readString(part, 'tool_use_id') ?? '',\n content: stringifyToolContent(rawContent),\n isError: readBoolean(part, 'is_error') ?? false,\n raw: rec,\n };\n }\n }\n }\n }\n\n if (type === 'result') {\n const usage = isRecord(rec['usage']) ? extractUsage(rec['usage']) : null;\n return {\n kind: 'result',\n text: readString(rec, 'result') ?? '',\n durationMs: readNumber(rec, 'duration_ms') ?? 0,\n usage,\n costUsd: readNumber(rec, 'total_cost_usd'),\n reason: readString(rec, 'terminal_reason') ?? readString(rec, 'stop_reason') ?? 'completed',\n isError: readBoolean(rec, 'is_error') ?? false,\n raw: rec,\n };\n }\n\n return {\n kind: 'unknown',\n summary: `Unrecognised claude frame type: ${typeof type === 'string' ? type : 'n/a'}`,\n raw: rec,\n };\n}\n\nfunction viewSystemHook(rec: Record<string, unknown>): ParsedView {\n // Hooks come through as a `Hook` event-kind on our side but the payload\n // shape matches claude's hook_started / hook_response stream-json.\n return viewFromClaudeFrame(rec);\n}\n\nfunction extractAssistantParts(content: unknown): readonly ParsedAssistantPart[] {\n if (!Array.isArray(content)) {\n return [];\n }\n const parts: ParsedAssistantPart[] = [];\n for (const part of content) {\n if (!isRecord(part)) {\n continue;\n }\n if (part['type'] === 'text') {\n const text = readString(part, 'text');\n if (text !== null) {\n parts.push({ type: 'text', text });\n }\n } else if (part['type'] === 'tool_use') {\n parts.push({\n type: 'tool_use',\n id: readString(part, 'id') ?? '',\n toolName: readString(part, 'name') ?? 'tool',\n input: part['input'] ?? null,\n });\n }\n }\n return parts;\n}\n\nfunction extractUsage(usage: Record<string, unknown>): ParsedTokenUsage {\n return {\n input: readNumber(usage, 'input_tokens') ?? 0,\n output: readNumber(usage, 'output_tokens') ?? 0,\n cacheRead: readNumber(usage, 'cache_read_input_tokens') ?? undefined,\n cacheCreation: readNumber(usage, 'cache_creation_input_tokens') ?? undefined,\n };\n}\n\n/**\n * Tool result `content` comes in many shapes: a plain string, an array of\n * `{type:'text', text:...}` blocks (the Anthropic content-block format), or\n * raw JSON. Flatten everything we recognise into a single string for the\n * UI; the consumer can still pop the raw payload for inspection.\n */\nfunction stringifyToolContent(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (Array.isArray(content)) {\n return content\n .map((block) => {\n if (isRecord(block) && block['type'] === 'text') {\n return readString(block, 'text') ?? '';\n }\n return JSON.stringify(block);\n })\n .join('\\n');\n }\n if (content === undefined || content === null) {\n return '';\n }\n try {\n return JSON.stringify(content, null, 2);\n } catch {\n return String(content);\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n input,\n signal,\n} from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { AiSessionEventDto } from '../../models/ai-session-event';\nimport { ParsedEvent, parseAndOrder } from './event-parser';\n\n/**\n * Read-only transcript of a session, derived from the events stream.\n *\n * The component takes raw `AiSessionEventDto` entries, runs them through the\n * `event-parser` to produce a typed `ParsedView` per event, and renders each\n * kind with its own treatment — assistant bubbles for chat text, expandable\n * cards for tool calls + tool results, single-line collapsed strips for the\n * system / hook noise, and a stats panel for the terminal `result` summary.\n *\n * Markdown rendering inside assistant text is still deferred (concept §10);\n * for now the parser hands plain text to the template and the SCSS preserves\n * line breaks via `white-space: pre-wrap`. Picking a renderer (markdown-it vs\n * marked) is gated on a CSP review and a markdown-corpus regression test.\n */\n@Component({\n selector: 'mm-ai-chat-stream',\n standalone: true,\n imports: [CommonModule],\n templateUrl: './ai-chat-stream.component.html',\n styleUrl: './ai-chat-stream.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AiChatStreamComponent {\n readonly events = input.required<AiSessionEventDto[]>();\n\n /**\n * Sorted + parsed. Recomputes whenever the input signal changes; the sort\n * is stable so live appends from SignalR keep prior render positions.\n */\n protected readonly parsedEvents = computed<ParsedEvent[]>(() =>\n parseAndOrder(this.events()),\n );\n\n /**\n * Per-event \"show raw JSON\" toggle. Keyed by sequence so live appends\n * don't flicker existing toggles closed. A `Set<number>` rather than a\n * record so add / delete avoids `keyof` shenanigans.\n */\n private readonly expandedSet = signal<ReadonlySet<number>>(new Set());\n\n protected isExpanded(sequence: number): boolean {\n return this.expandedSet().has(sequence);\n }\n\n protected toggleRaw(sequence: number): void {\n this.expandedSet.update((prev) => {\n const next = new Set(prev);\n if (next.has(sequence)) {\n next.delete(sequence);\n } else {\n next.add(sequence);\n }\n return next;\n });\n }\n\n /**\n * `HH:MM:SS` formatter — full ISO is too noisy in a 30-event transcript.\n * Returns the input unchanged if parsing fails so we don't pretend events\n * we can't read are recent.\n */\n protected formatTime(iso: string): string {\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) {\n return iso;\n }\n return d.toLocaleTimeString('de-AT', { hour12: false });\n }\n\n /** Compact \"1234 tok\" for the assistant header. */\n protected formatTokenCount(n: number): string {\n if (n >= 1000) {\n return `${(n / 1000).toFixed(1)} ktok`;\n }\n return `${n} tok`;\n }\n\n /** Duration in either `ms` (sub-second) or `s.SS`. */\n protected formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)} ms`;\n }\n return `${(ms / 1000).toFixed(2)} s`;\n }\n\n /** Cost in EUR-style decimals; null + zero collapse to a dash. */\n protected formatCost(usd: number | null | undefined): string {\n if (usd == null || usd === 0) {\n return '—';\n }\n return `$${usd.toFixed(4)}`;\n }\n\n /** Pretty-print arbitrary tool_use input or raw blocks for the expandable detail panel. */\n protected formatRaw(value: unknown): string {\n if (value === undefined || value === null) {\n return '';\n }\n if (typeof value === 'string') {\n return value;\n }\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n }\n}\n","<div class=\"mm-ai-chat\">\n @for (parsed of parsedEvents(); track parsed.event.sequence) {\n @switch (parsed.view.kind) {\n @case ('assistant') {\n <article class=\"mm-ai-chat__bubble mm-ai-chat__bubble--assistant\">\n <header class=\"mm-ai-chat__bubble-header\">\n <span class=\"mm-ai-chat__role\">Assistant</span>\n @if (parsed.view.model; as model) {\n <span class=\"mm-ai-chat__meta\">{{ model }}</span>\n }\n @if (parsed.view.usage; as usage) {\n <span class=\"mm-ai-chat__meta\">\n {{ formatTokenCount(usage.input + usage.output) }}\n </span>\n }\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </header>\n @for (part of parsed.view.parts; track $index) {\n @switch (part.type) {\n @case ('text') {\n <p class=\"mm-ai-chat__text\">{{ part.text }}</p>\n }\n @case ('tool_use') {\n <div class=\"mm-ai-chat__tool-use\">\n <header class=\"mm-ai-chat__tool-use-header\">\n <span class=\"mm-ai-chat__tool-badge\">tool</span>\n <span class=\"mm-ai-chat__tool-name\">{{ part.toolName }}</span>\n </header>\n <pre class=\"mm-ai-chat__tool-input\">{{ formatRaw(part.input) }}</pre>\n </div>\n }\n }\n }\n </article>\n }\n\n @case ('tool-result') {\n <article\n class=\"mm-ai-chat__tool-result\"\n [class.mm-ai-chat__tool-result--error]=\"parsed.view.isError\"\n >\n <header class=\"mm-ai-chat__tool-result-header\">\n <span class=\"mm-ai-chat__role\">Tool result</span>\n @if (parsed.view.isError) {\n <span class=\"mm-ai-chat__error-badge\">error</span>\n }\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </header>\n <pre class=\"mm-ai-chat__tool-result-body\">{{ parsed.view.content }}</pre>\n </article>\n }\n\n @case ('result') {\n <article class=\"mm-ai-chat__result\"\n [class.mm-ai-chat__result--error]=\"parsed.view.isError\">\n <header class=\"mm-ai-chat__result-header\">\n <span class=\"mm-ai-chat__role\">{{ parsed.view.reason }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </header>\n @if (parsed.view.text) {\n <p class=\"mm-ai-chat__result-text\">{{ parsed.view.text }}</p>\n }\n <dl class=\"mm-ai-chat__result-stats\">\n <div>\n <dt>Duration</dt>\n <dd>{{ formatDuration(parsed.view.durationMs) }}</dd>\n </div>\n <div>\n <dt>Tokens (in/out)</dt>\n <dd>\n @if (parsed.view.usage) {\n {{ formatTokenCount(parsed.view.usage.input) }}\n /\n {{ formatTokenCount(parsed.view.usage.output) }}\n } @else {\n —\n }\n </dd>\n </div>\n <div>\n <dt>Cost</dt>\n <dd>{{ formatCost(parsed.view.costUsd) }}</dd>\n </div>\n </dl>\n </article>\n }\n\n @case ('system') {\n <button\n type=\"button\"\n class=\"mm-ai-chat__system\"\n [class.mm-ai-chat__system--init]=\"parsed.view.subKind === 'init'\"\n [class.mm-ai-chat__system--hook]=\"parsed.view.subKind === 'hook'\"\n [class.mm-ai-chat__system--rate]=\"parsed.view.subKind === 'rate-limit'\"\n [attr.aria-expanded]=\"isExpanded(parsed.event.sequence)\"\n (click)=\"toggleRaw(parsed.event.sequence)\"\n >\n <span class=\"mm-ai-chat__system-chevron\">\n {{ isExpanded(parsed.event.sequence) ? '▾' : '▸' }}\n </span>\n <span class=\"mm-ai-chat__system-summary\">{{ parsed.view.summary }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </button>\n @if (isExpanded(parsed.event.sequence)) {\n <pre class=\"mm-ai-chat__system-detail\">{{ formatRaw(parsed.view.raw) }}</pre>\n }\n }\n\n @case ('status-change') {\n <div class=\"mm-ai-chat__status\">\n <span class=\"mm-ai-chat__status-label\">Status</span>\n <span class=\"mm-ai-chat__status-value\">{{ parsed.view.status }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </div>\n }\n\n @case ('unknown') {\n <button\n type=\"button\"\n class=\"mm-ai-chat__system mm-ai-chat__system--unknown\"\n [attr.aria-expanded]=\"isExpanded(parsed.event.sequence)\"\n (click)=\"toggleRaw(parsed.event.sequence)\"\n >\n <span class=\"mm-ai-chat__system-chevron\">\n {{ isExpanded(parsed.event.sequence) ? '▾' : '▸' }}\n </span>\n <span class=\"mm-ai-chat__system-summary\">{{ parsed.view.summary }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </button>\n @if (isExpanded(parsed.event.sequence)) {\n <pre class=\"mm-ai-chat__system-detail\">{{ formatRaw(parsed.view.raw) }}</pre>\n }\n }\n }\n } @empty {\n <div class=\"mm-ai-chat__empty\">\n No events yet — the worker hasn't emitted anything.\n </div>\n }\n</div>\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n input,\n signal,\n} from '@angular/core';\nimport { AiToolCallDto } from '../../models/ai-session-event';\n\n/**\n * Expandable card surface for one tool invocation in a session transcript.\n * Click the header to toggle arguments + result; the body is rendered as\n * pretty-printed JSON so the host doesn't need its own json formatter.\n */\n@Component({\n selector: 'mm-ai-tool-call',\n imports: [],\n templateUrl: './ai-tool-call.component.html',\n styleUrl: './ai-tool-call.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AiToolCallComponent {\n readonly call = input.required<AiToolCallDto>();\n\n protected readonly expanded = signal(false);\n\n protected readonly statusVariant = computed(() => {\n const status = this.call().status;\n switch (status) {\n case 'Succeeded':\n return 'succeeded';\n case 'Failed':\n return 'failed';\n case 'Rejected':\n return 'rejected';\n case 'Approved':\n case 'Pending':\n return 'pending';\n default:\n return 'pending';\n }\n });\n\n protected readonly argumentsJson = computed(() =>\n this.format(this.call().arguments),\n );\n protected readonly resultJson = computed(() => {\n const result = this.call().result;\n return result === undefined ? null : this.format(result);\n });\n\n protected toggleExpanded(): void {\n this.expanded.update((v) => !v);\n }\n\n private format(value: unknown): string {\n if (value === null || value === undefined) {\n return '';\n }\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n }\n}\n","<div class=\"mm-ai-tool-call mm-ai-tool-call--{{ statusVariant() }}\">\n <button\n type=\"button\"\n class=\"mm-ai-tool-call__header\"\n (click)=\"toggleExpanded()\"\n [attr.aria-expanded]=\"expanded()\"\n >\n <span class=\"mm-ai-tool-call__chevron\">\n {{ expanded() ? '▾' : '▸' }}\n </span>\n <span class=\"mm-ai-tool-call__name\">{{ call().toolName }}</span>\n <span class=\"mm-ai-tool-call__status\">{{ call().status }}</span>\n @if (call().durationMs != null) {\n <span class=\"mm-ai-tool-call__duration\">\n {{ call().durationMs }}ms\n </span>\n }\n </button>\n\n @if (expanded()) {\n <div class=\"mm-ai-tool-call__body\">\n <div class=\"mm-ai-tool-call__section\">\n <h4 class=\"mm-ai-tool-call__heading\">Arguments</h4>\n <pre class=\"mm-ai-tool-call__code\">{{ argumentsJson() }}</pre>\n </div>\n\n @if (resultJson() !== null) {\n <div class=\"mm-ai-tool-call__section\">\n <h4 class=\"mm-ai-tool-call__heading\">Result</h4>\n <pre class=\"mm-ai-tool-call__code\">{{ resultJson() }}</pre>\n </div>\n }\n </div>\n }\n</div>\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport {\n AiApprovalDecisionDto,\n AiApprovalRequestedDto,\n} from '../../models/ai-approval';\n\n/**\n * Approval modal. The host opens it when a `AiApprovalRequestedDto` arrives on\n * the SignalR stream, fills the `request` input, and listens for the `decide`\n * output to call `AiAdapterClientService.decideApproval`. The modal itself is\n * presentation-only: no networking, no global state, no portal magic.\n *\n * The host is expected to wrap the modal in its own backdrop / focus-trap so\n * the library doesn't pull in a positioning dependency (CDK overlay, Kendo\n * window, ng-bootstrap, …) — each host shop already has one.\n */\n@Component({\n selector: 'mm-ai-approval-modal',\n imports: [FormsModule],\n templateUrl: './ai-approval-modal.component.html',\n styleUrl: './ai-approval-modal.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AiApprovalModalComponent {\n readonly request = input.required<AiApprovalRequestedDto>();\n\n readonly decide = output<AiApprovalDecisionDto>();\n readonly dismiss = output<void>();\n\n protected readonly comment = signal('');\n\n protected readonly prettyPayload = computed(() => {\n try {\n return JSON.stringify(JSON.parse(this.request().payload), null, 2);\n } catch {\n return this.request().payload;\n }\n });\n\n protected approve(): void {\n this.decide.emit({\n outcome: 'Approved',\n comment: this.commentOrUndefined(),\n });\n }\n\n protected reject(): void {\n this.decide.emit({\n outcome: 'Rejected',\n comment: this.commentOrUndefined(),\n });\n }\n\n protected onDismiss(): void {\n this.dismiss.emit();\n }\n\n private commentOrUndefined(): string | undefined {\n const trimmed = this.comment().trim();\n return trimmed.length > 0 ? trimmed : undefined;\n }\n}\n","<div class=\"mm-ai-approval\">\n <header class=\"mm-ai-approval__header\">\n <h3 class=\"mm-ai-approval__title\">Approve tool call</h3>\n <button\n type=\"button\"\n class=\"mm-ai-approval__close\"\n (click)=\"onDismiss()\"\n aria-label=\"Dismiss\"\n >\n ×\n </button>\n </header>\n\n <div class=\"mm-ai-approval__meta\">\n <div><span>Tool:</span> <code>{{ request().toolName }}</code></div>\n <div><span>Reason:</span> <code>{{ request().reason }}</code></div>\n <div><span>Session:</span> <code>{{ request().sessionId }}</code></div>\n </div>\n\n <div class=\"mm-ai-approval__section\">\n <h4 class=\"mm-ai-approval__heading\">Payload</h4>\n <pre class=\"mm-ai-approval__code\">{{ prettyPayload() }}</pre>\n </div>\n\n <div class=\"mm-ai-approval__section\">\n <h4 class=\"mm-ai-approval__heading\">Comment <span>(optional)</span></h4>\n <textarea\n class=\"mm-ai-approval__comment\"\n [ngModel]=\"comment()\"\n (ngModelChange)=\"comment.set($event)\"\n rows=\"3\"\n placeholder=\"Why is this approved or rejected?\"\n ></textarea>\n </div>\n\n <footer class=\"mm-ai-approval__actions\">\n <button\n type=\"button\"\n class=\"mm-ai-approval__btn mm-ai-approval__btn--reject\"\n (click)=\"reject()\"\n >\n Reject\n </button>\n <button\n type=\"button\"\n class=\"mm-ai-approval__btn mm-ai-approval__btn--approve\"\n (click)=\"approve()\"\n >\n Approve\n </button>\n </footer>\n</div>\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\n\n/**\n * Two-bar usage indicator (daily + monthly) with threshold colour state.\n * Inputs are unit-agnostic counts and caps; the component does not parse the\n * tenant's `AiQuotaLimit` itself — the host is expected to feed numbers the\n * adapter already aggregated.\n */\n@Component({\n selector: 'mm-ai-quota-indicator',\n imports: [],\n templateUrl: './ai-quota-indicator.component.html',\n styleUrl: './ai-quota-indicator.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AiQuotaIndicatorComponent {\n readonly dailyConsumed = input.required<number>();\n readonly dailyCap = input.required<number>();\n readonly monthlyConsumed = input<number | null>(null);\n readonly monthlyCap = input<number | null>(null);\n\n protected readonly dailyPercent = computed(() =>\n this.percent(this.dailyConsumed(), this.dailyCap()),\n );\n protected readonly monthlyPercent = computed(() => {\n const consumed = this.monthlyConsumed();\n const cap = this.monthlyCap();\n if (consumed == null || cap == null) {\n return null;\n }\n return this.percent(consumed, cap);\n });\n\n protected readonly dailyState = computed(() => this.toState(this.dailyPercent()));\n protected readonly monthlyState = computed(() => {\n const pct = this.monthlyPercent();\n return pct == null ? null : this.toState(pct);\n });\n\n private percent(consumed: number, cap: number): number {\n if (cap <= 0) {\n return 0;\n }\n return Math.min(100, Math.round((consumed / cap) * 100));\n }\n\n private toState(percent: number): 'ok' | 'warning' | 'critical' {\n if (percent >= 100) {\n return 'critical';\n }\n if (percent >= 80) {\n return 'warning';\n }\n return 'ok';\n }\n}\n","<div class=\"mm-ai-quota\">\n <div class=\"mm-ai-quota__row\">\n <span class=\"mm-ai-quota__label\">Daily</span>\n <div class=\"mm-ai-quota__bar mm-ai-quota__bar--{{ dailyState() }}\">\n <div class=\"mm-ai-quota__fill\" [style.width.%]=\"dailyPercent()\"></div>\n </div>\n <span class=\"mm-ai-quota__count\">\n {{ dailyConsumed() }} / {{ dailyCap() }} ({{ dailyPercent() }}%)\n </span>\n </div>\n\n @if (monthlyPercent() !== null) {\n <div class=\"mm-ai-quota__row\">\n <span class=\"mm-ai-quota__label\">Monthly</span>\n <div class=\"mm-ai-quota__bar mm-ai-quota__bar--{{ monthlyState() }}\">\n <div class=\"mm-ai-quota__fill\" [style.width.%]=\"monthlyPercent()\"></div>\n </div>\n <span class=\"mm-ai-quota__count\">\n {{ monthlyConsumed() }} / {{ monthlyCap() }} ({{ monthlyPercent() }}%)\n </span>\n </div>\n }\n</div>\n","import { CommonModule } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n inject,\n signal,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { AiAdapterClientService } from '../../services/ai-adapter-client.service';\nimport {\n AiCredentialTicketScope,\n IssueAiCredentialTicketResponseDto,\n} from '../../models/ai-credential-ticket';\n\n/**\n * Self-service credential-ticket issuer (#4133, concept §10). Admin picks a\n * scope + TTL, hits **Issue ticket**, copies the displayed code, hands it to\n * a developer / operator out-of-band. The plaintext code is shown exactly\n * once — refreshing the panel after issue clears it so a careless screenshot\n * doesn't leak.\n *\n * Theme-neutral by design: visuals use CSS custom properties with neutral\n * defaults so the host (Refinery Studio LCARS, demo-app, etc.) can override\n * them via `--mm-ai-ticket-*` variables without touching the component.\n */\n@Component({\n selector: 'mm-ai-credential-ticket-issue',\n imports: [CommonModule, FormsModule],\n templateUrl: './ai-credential-ticket-issue.component.html',\n styleUrl: './ai-credential-ticket-issue.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AiCredentialTicketIssueComponent {\n private readonly client = inject(AiAdapterClientService);\n\n protected readonly scope = signal<AiCredentialTicketScope>(\n 'CredentialRegister',\n );\n protected readonly ttlMinutes = signal<number>(5);\n\n protected readonly issuing = signal<boolean>(false);\n protected readonly issued = signal<IssueAiCredentialTicketResponseDto | null>(\n null,\n );\n protected readonly error = signal<string | null>(null);\n protected readonly copied = signal<boolean>(false);\n\n /** Available scope choices — kept in sync with `AiCredentialTicketScope`. */\n protected readonly scopes: readonly {\n value: AiCredentialTicketScope;\n label: string;\n hint: string;\n }[] = [\n {\n value: 'CredentialRegister',\n label: 'Subscription token',\n hint: 'For the bastion CLI to register an Anthropic OAuth pair on this tenant.',\n },\n {\n value: 'DevSshKeyRegister',\n label: 'Developer SSH key',\n hint: 'For a developer to register their own SSH public key on this tenant.',\n },\n ];\n\n protected onIssue(): void {\n if (this.issuing()) {\n return;\n }\n const ttl = this.normalizedTtl();\n this.issuing.set(true);\n this.error.set(null);\n this.copied.set(false);\n this.issued.set(null);\n\n this.client\n .issueCredentialTicket({\n scope: this.scope(),\n ttlMinutes: ttl,\n })\n .subscribe({\n next: (response) => {\n this.issued.set(response);\n this.issuing.set(false);\n },\n error: (err: unknown) => {\n this.error.set(this.formatError(err));\n this.issuing.set(false);\n },\n });\n }\n\n protected onCopy(): void {\n const code = this.issued()?.code;\n if (!code) {\n return;\n }\n // navigator.clipboard requires a secure context; if it's unavailable the\n // operator can still select + copy by hand. Falling back silently keeps\n // the panel usable on http://localhost dev origins.\n if (navigator.clipboard && navigator.clipboard.writeText) {\n navigator.clipboard.writeText(code).then(\n () => this.copied.set(true),\n () => this.copied.set(false),\n );\n }\n }\n\n protected onClear(): void {\n this.issued.set(null);\n this.copied.set(false);\n }\n\n private normalizedTtl(): number {\n const v = Math.trunc(this.ttlMinutes());\n // Server clamps to 60 anyway; clamp here too so the slider can't push a\n // value the request would silently truncate.\n if (Number.isNaN(v) || v < 1) {\n return 1;\n }\n if (v > 60) {\n return 60;\n }\n return v;\n }\n\n private formatError(err: unknown): string {\n if (\n typeof err === 'object' &&\n err !== null &&\n 'status' in err &&\n typeof (err as { status: number }).status === 'number'\n ) {\n const status = (err as { status: number }).status;\n if (status === 401 || status === 403) {\n return 'Not authorised — only tenant admins can issue tickets.';\n }\n if (status === 400) {\n return 'Invalid request — check the scope value.';\n }\n return `Server returned HTTP ${status}.`;\n }\n return 'Failed to reach the AI Adapter — check the network.';\n }\n}\n","<section class=\"mm-ai-ticket\">\n <header class=\"mm-ai-ticket__header\">\n <div class=\"mm-ai-ticket__title\">Issue credential ticket</div>\n <div class=\"mm-ai-ticket__subtitle\">\n Mint a one-time code an operator redeems on their own machine.\n </div>\n </header>\n\n @if (!issued()) {\n <form\n class=\"mm-ai-ticket__form\"\n (ngSubmit)=\"onIssue()\"\n #issueForm=\"ngForm\"\n >\n <label class=\"mm-ai-ticket__field\">\n <span class=\"mm-ai-ticket__label\">Scope</span>\n <select\n class=\"mm-ai-ticket__select\"\n [ngModel]=\"scope()\"\n (ngModelChange)=\"scope.set($event)\"\n name=\"scope\"\n [disabled]=\"issuing()\"\n >\n @for (s of scopes; track s.value) {\n <option [value]=\"s.value\">{{ s.label }}</option>\n }\n </select>\n </label>\n <p class=\"mm-ai-ticket__hint\">\n @for (s of scopes; track s.value) {\n @if (s.value === scope()) {\n {{ s.hint }}\n }\n }\n </p>\n\n <label class=\"mm-ai-ticket__field\">\n <span class=\"mm-ai-ticket__label\">TTL (minutes)</span>\n <input\n class=\"mm-ai-ticket__input\"\n type=\"number\"\n min=\"1\"\n max=\"60\"\n step=\"1\"\n [ngModel]=\"ttlMinutes()\"\n (ngModelChange)=\"ttlMinutes.set(+$event)\"\n name=\"ttlMinutes\"\n [disabled]=\"issuing()\"\n />\n </label>\n <p class=\"mm-ai-ticket__hint\">\n Default 5 minutes. Server clamps to 60 — long TTLs leak.\n </p>\n\n <div class=\"mm-ai-ticket__actions\">\n <button\n class=\"mm-ai-ticket__button mm-ai-ticket__button--primary\"\n type=\"submit\"\n [disabled]=\"issuing()\"\n >\n @if (issuing()) {\n Issuing…\n } @else {\n Issue ticket\n }\n </button>\n </div>\n\n @if (error()) {\n <p class=\"mm-ai-ticket__error\">{{ error() }}</p>\n }\n </form>\n } @else {\n <div class=\"mm-ai-ticket__result\">\n <p class=\"mm-ai-ticket__result-label\">\n One-time code (copy now — not shown again):\n </p>\n <div class=\"mm-ai-ticket__code-row\">\n <code class=\"mm-ai-ticket__code\">{{ issued()!.code }}</code>\n <button\n class=\"mm-ai-ticket__button mm-ai-ticket__button--secondary\"\n type=\"button\"\n (click)=\"onCopy()\"\n >\n @if (copied()) {\n Copied\n } @else {\n Copy\n }\n </button>\n </div>\n <dl class=\"mm-ai-ticket__meta\">\n <div>\n <dt>Scope</dt>\n <dd>{{ issued()!.scope }}</dd>\n </div>\n <div>\n <dt>Expires at</dt>\n <dd>{{ issued()!.expiresAt }}</dd>\n </div>\n <div>\n <dt>Ticket rtId</dt>\n <dd><code>{{ issued()!.rtId }}</code></dd>\n </div>\n </dl>\n <div class=\"mm-ai-ticket__actions\">\n <button\n class=\"mm-ai-ticket__button\"\n type=\"button\"\n (click)=\"onClear()\"\n >\n Issue another\n </button>\n </div>\n </div>\n }\n</section>\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { AiAppTemplateDto, AiNewAppSubmission } from '../../models/ai-app-template';\n\n/**\n * \"New App from Template\" wizard (#4145). Presentation-only — the host fetches\n * the template catalogue via {@link AiAdapterClientService.getAppTemplates},\n * feeds it into the `templates` input, and listens for `submit` / `cancel` to\n * call `createSession`.\n *\n * The component is intentionally framework-agnostic (no Kendo, no CDK overlay)\n * so consumers can wrap it in whatever modal shell they already have — same\n * convention as `AiApprovalModalComponent`.\n *\n * The interpolation engine is *deliberately* dumb: a regex substitution over\n * `{{placeholder}}` tokens. The wizard only collects two inputs (project name +\n * primary entity), and a full Handlebars dependency for two tokens would be\n * overkill. If a future template needs richer rendering, switch this to\n * `handlebars.precompile`-at-build-time and re-evaluate.\n */\n@Component({\n selector: 'mm-ai-new-app-dialog',\n imports: [FormsModule],\n templateUrl: './ai-new-app-dialog.component.html',\n styleUrl: './ai-new-app-dialog.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AiNewAppDialogComponent {\n /** Available templates from the adapter — typically 3 in Phase 1. */\n readonly templates = input.required<readonly AiAppTemplateDto[]>();\n\n /** Tenant name shown in the dialog header so the operator confirms the scope. */\n readonly tenantName = input<string>('');\n\n /**\n * Emitted on Submit with the fully interpolated Goal + the picked template id.\n * Named `confirmed` (not `submit`) because Angular flags native-event collisions\n * — `submit` is a DOM event and triggers `@angular-eslint/no-output-native`.\n */\n readonly confirmed = output<AiNewAppSubmission>();\n\n /**\n * Emitted on Cancel / dismiss. The host hides the wizard on this signal.\n * Named `cancelled` for the same reason `confirmed` is not `submit`.\n */\n readonly cancelled = output<void>();\n\n /** rtId-style id of the picked template, or empty until the operator picks one. */\n protected readonly selectedTemplateId = signal('');\n protected readonly projectName = signal('');\n protected readonly primaryEntity = signal('');\n\n protected readonly selectedTemplate = computed<AiAppTemplateDto | null>(() => {\n const id = this.selectedTemplateId();\n return this.templates().find((t) => t.id === id) ?? null;\n });\n\n /**\n * Rendered Goal string the operator sees in the preview block. Stays empty\n * until a template is picked AND every required placeholder has a value, so\n * the operator never sees a half-interpolated string.\n */\n protected readonly renderedGoal = computed(() => {\n const template = this.selectedTemplate();\n if (!template) {\n return '';\n }\n if (!this.allRequiredFilled()) {\n return '';\n }\n return this.interpolate(template.goalTemplate);\n });\n\n /** True when every placeholder the picked template declares has a non-empty value. */\n protected readonly allRequiredFilled = computed(() => {\n const template = this.selectedTemplate();\n if (!template) {\n return false;\n }\n return template.requiredPlaceholders.every((key) => {\n const value = this.valueFor(key);\n return value !== null && value.trim().length > 0;\n });\n });\n\n protected onSubmit(): void {\n const template = this.selectedTemplate();\n if (!template || !this.allRequiredFilled()) {\n return;\n }\n this.confirmed.emit({\n templateId: template.id,\n jobKind: template.jobKind,\n projectName: this.projectName().trim(),\n primaryEntity: this.primaryEntity().trim(),\n goal: this.interpolate(template.goalTemplate),\n });\n }\n\n protected onCancel(): void {\n this.cancelled.emit();\n }\n\n /** Read the signal that backs the named placeholder, or `null` if unknown. */\n private valueFor(placeholder: string): string | null {\n switch (placeholder) {\n case 'projectName':\n return this.projectName();\n case 'primaryEntity':\n return this.primaryEntity();\n default:\n return null;\n }\n }\n\n private interpolate(template: string): string {\n return template.replace(/{{\\s*(\\w+)\\s*}}/g, (full, key) => {\n const value = this.valueFor(key);\n // Unknown placeholders survive verbatim so a typo in the catalogue\n // surfaces in the preview rather than silently disappearing.\n return value !== null ? value.trim() : full;\n });\n }\n}\n","<div class=\"mm-ai-new-app\">\n <header class=\"mm-ai-new-app__header\">\n <h3 class=\"mm-ai-new-app__title\">\n New App\n @if (tenantName(); as tenant) {\n <span class=\"mm-ai-new-app__tenant\"> · {{ tenant }}</span>\n }\n </h3>\n <button\n type=\"button\"\n class=\"mm-ai-new-app__close\"\n (click)=\"onCancel()\"\n aria-label=\"Dismiss\"\n >\n ×\n </button>\n </header>\n\n <div class=\"mm-ai-new-app__section\">\n <h4 class=\"mm-ai-new-app__heading\">Template</h4>\n <ul class=\"mm-ai-new-app__templates\" role=\"radiogroup\" aria-label=\"Template\">\n @for (template of templates(); track template.id) {\n <li>\n <label\n class=\"mm-ai-new-app__template\"\n [class.mm-ai-new-app__template--active]=\"\n selectedTemplateId() === template.id\n \"\n >\n <input\n type=\"radio\"\n name=\"mm-ai-new-app-template\"\n [value]=\"template.id\"\n [checked]=\"selectedTemplateId() === template.id\"\n (change)=\"selectedTemplateId.set(template.id)\"\n />\n <span class=\"mm-ai-new-app__template-body\">\n <strong class=\"mm-ai-new-app__template-name\">{{ template.name }}</strong>\n <span class=\"mm-ai-new-app__template-description\">\n {{ template.description }}\n </span>\n </span>\n </label>\n </li>\n }\n </ul>\n </div>\n\n @if (selectedTemplate(); as template) {\n <div class=\"mm-ai-new-app__section\">\n <h4 class=\"mm-ai-new-app__heading\">Parameters</h4>\n <div class=\"mm-ai-new-app__field\">\n <label for=\"mm-ai-new-app-project-name\">Project name</label>\n <input\n id=\"mm-ai-new-app-project-name\"\n type=\"text\"\n [ngModel]=\"projectName()\"\n (ngModelChange)=\"projectName.set($event)\"\n placeholder=\"e.g. customer-list\"\n autocomplete=\"off\"\n />\n </div>\n <div class=\"mm-ai-new-app__field\">\n <label for=\"mm-ai-new-app-primary-entity\">Primary entity (CK type)</label>\n <input\n id=\"mm-ai-new-app-primary-entity\"\n type=\"text\"\n [ngModel]=\"primaryEntity()\"\n (ngModelChange)=\"primaryEntity.set($event)\"\n placeholder=\"e.g. Customer\"\n autocomplete=\"off\"\n />\n </div>\n </div>\n\n <div class=\"mm-ai-new-app__section\">\n <h4 class=\"mm-ai-new-app__heading\">\n Generated Goal <span>(this is what Claude will see)</span>\n </h4>\n <pre class=\"mm-ai-new-app__preview\">{{\n renderedGoal() ||\n 'Fill in every field above to preview the Goal that will be sent.'\n }}</pre>\n </div>\n }\n\n <footer class=\"mm-ai-new-app__actions\">\n <button\n type=\"button\"\n class=\"mm-ai-new-app__btn mm-ai-new-app__btn--cancel\"\n (click)=\"onCancel()\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mm-ai-new-app__btn mm-ai-new-app__btn--submit\"\n [disabled]=\"!allRequiredFilled()\"\n (click)=\"onSubmit()\"\n >\n Create App Session\n </button>\n </footer>\n</div>\n","/*\n * Public API Surface of @meshmakers/octo-ai-console\n *\n * Reusable Angular building blocks for the OctoMesh AI Adapter session console\n * (ADR-21, issue #4121). Host applications import individual standalone\n * components or call `provideOctoAiConsole({...})` to wire the services.\n */\n\nexport * from './lib/models';\nexport * from './lib/services';\nexport * from './lib/components';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"meshmakers-octo-ai-console.mjs","sources":["../../../../projects/meshmakers/octo-ai-console/src/lib/services/ai-adapter-client.service.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/services/ai-session-stream.service.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/services/ai-adapter-options.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-job-status-badge/ai-job-status-badge.component.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-job-status-badge/ai-job-status-badge.component.html","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-session-list/ai-session-list.component.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-session-list/ai-session-list.component.html","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-chat-stream/event-parser.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-chat-stream/ai-chat-stream.component.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-chat-stream/ai-chat-stream.component.html","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-tool-call/ai-tool-call.component.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-tool-call/ai-tool-call.component.html","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-approval-modal/ai-approval-modal.component.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-approval-modal/ai-approval-modal.component.html","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-quota-indicator/ai-quota-indicator.component.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-quota-indicator/ai-quota-indicator.component.html","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-credential-ticket-issue/ai-credential-ticket-issue.component.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-credential-ticket-issue/ai-credential-ticket-issue.component.html","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-new-app-dialog/ai-new-app-dialog.component.ts","../../../../projects/meshmakers/octo-ai-console/src/lib/components/ai-new-app-dialog/ai-new-app-dialog.component.html","../../../../projects/meshmakers/octo-ai-console/src/public-api.ts","../../../../projects/meshmakers/octo-ai-console/src/meshmakers-octo-ai-console.ts"],"sourcesContent":["import { HttpClient, HttpParams } from '@angular/common/http';\nimport { Injectable, inject } from '@angular/core';\nimport { Observable } from 'rxjs';\nimport { AI_ADAPTER_OPTIONS } from './ai-adapter-options';\nimport {\n AiSessionDto,\n CreateSessionRequestDto,\n CreateSessionResponseDto,\n} from '../models/ai-session';\nimport { AiSessionEventDto } from '../models/ai-session-event';\nimport { AiApprovalDecisionDto } from '../models/ai-approval';\nimport {\n IssueAiCredentialTicketRequestDto,\n IssueAiCredentialTicketResponseDto,\n} from '../models/ai-credential-ticket';\nimport {\n AiGitHubPatDto,\n RegisterAiGitHubPatRequestDto,\n} from '../models/ai-github-pat';\nimport { AiAppTemplateDto } from '../models/ai-app-template';\n\n/**\n * Typed REST client for the AI Adapter's session endpoints. Every method maps\n * 1:1 to a controller route on the C# side\n * (`src/AiServices/TenantApi/v1/Controllers/SessionsController.cs`).\n *\n * The service does NOT cache responses or hold session state — each call is a\n * pure pass-through to `HttpClient`. Components that need a derived view (e.g.\n * a session list with status counts) should compose over the returned\n * observables themselves; that keeps the client free of UI concerns and lets\n * host apps mix Apollo / GraphQL state in the same component without dueling\n * caches.\n */\n/**\n * Not `providedIn: 'root'` — `AI_ADAPTER_OPTIONS` is provided per-route in host\n * apps (the tenant id is read from the route param, which the root injector\n * doesn't see). A root-provided service would resolve through the root injector\n * and fail with NG0201 on `AI_ADAPTER_OPTIONS`. Standalone components that\n * inject this service pick up the route-level provider via the element\n * injector hierarchy.\n */\n@Injectable()\nexport class AiAdapterClientService {\n private readonly http = inject(HttpClient);\n private readonly options = inject(AI_ADAPTER_OPTIONS);\n\n /**\n * `GET /{tenantId}/v1/sessions` — list all sessions for the configured tenant.\n * Optional `status` filter narrows to one lifecycle state when present.\n */\n listSessions(status?: string): Observable<AiSessionDto[]> {\n const url = `${this.tenantBase()}/sessions`;\n let params = new HttpParams();\n if (status) {\n params = params.set('status', status);\n }\n return this.http.get<AiSessionDto[]>(url, { params });\n }\n\n /** `GET /{tenantId}/v1/sessions/{sessionId}` — single session by rtId. */\n getSession(sessionId: string): Observable<AiSessionDto> {\n return this.http.get<AiSessionDto>(\n `${this.tenantBase()}/sessions/${encodeURIComponent(sessionId)}`,\n );\n }\n\n /**\n * `POST /{tenantId}/v1/sessions` — start a new session. Returns the persisted\n * session plus the quota gate's decision so the caller can render either a\n * \"Running\" pill or a \"Queued (n)\" pill without a second round-trip.\n */\n createSession(\n request: CreateSessionRequestDto,\n ): Observable<CreateSessionResponseDto> {\n return this.http.post<CreateSessionResponseDto>(\n `${this.tenantBase()}/sessions`,\n request,\n );\n }\n\n /**\n * `POST /{tenantId}/v1/sessions/{sessionId}/cancel` — request graceful\n * cancellation; the orchestrator transitions the session to `Cancelled`\n * once the worker has acknowledged.\n */\n cancelSession(sessionId: string): Observable<AiSessionDto> {\n return this.http.post<AiSessionDto>(\n `${this.tenantBase()}/sessions/${encodeURIComponent(sessionId)}/cancel`,\n null,\n );\n }\n\n /**\n * `DELETE /{tenantId}/v1/sessions/{sessionId}` — hard-delete a terminal\n * session. The server refuses non-terminal sessions with HTTP 409 and the\n * <code>invalid_state_transition</code> error code, so callers should cancel\n * first when the session is still in flight. 204 on success; the response\n * has no body.\n */\n deleteSession(sessionId: string): Observable<void> {\n return this.http.delete<void>(\n `${this.tenantBase()}/sessions/${encodeURIComponent(sessionId)}`,\n );\n }\n\n /**\n * `POST /{tenantId}/v1/sessions/{sessionId}/messages` — append a follow-up\n * user turn to a `Completed` session. The adapter re-materialises the\n * workspace (fresh credential snapshot), recovers Claude's session id from\n * the persisted `system/init` event of the original turn, and spawns\n * <code>claude --resume <id></code> so the conversation context\n * carries over. The next turn's events stream on the same SignalR channel\n * as the prior turn — there is no separate subscription to set up.\n *\n * Server response is 202 with the updated session DTO (status flipped back\n * to `Running`); the caller can use the returned status to drive an\n * optimistic UI update. Non-Completed sessions return 409 with\n * <code>invalid_state_transition</code>; a session whose first turn died\n * before claude emitted init returns 409 with\n * <code>claude_session_id_not_captured</code>.\n */\n sendMessage(sessionId: string, text: string): Observable<AiSessionDto> {\n return this.http.post<AiSessionDto>(\n `${this.tenantBase()}/sessions/${encodeURIComponent(sessionId)}/messages`,\n { text },\n );\n }\n\n /**\n * `GET /{tenantId}/v1/sessions/{sessionId}/events?sinceSequence=N` — replay\n * persisted events for a session. UI uses this on reconnect to backfill any\n * events SignalR missed while disconnected (drives the resume-from-sequence\n * path in {@link AiSessionStreamService}).\n */\n listEvents(\n sessionId: string,\n sinceSequence?: number,\n ): Observable<AiSessionEventDto[]> {\n const url = `${this.tenantBase()}/sessions/${encodeURIComponent(sessionId)}/events`;\n let params = new HttpParams();\n if (sinceSequence != null) {\n params = params.set('sinceSequence', sinceSequence.toString());\n }\n return this.http.get<AiSessionEventDto[]>(url, { params });\n }\n\n /**\n * `POST /{tenantId}/v1/sessions/{sessionId}/approvals/{requestId}` — submit\n * the user's decision on a paused approval gate. Server fans\n * `OnApprovalDecidedAsync` to every connection on the session.\n */\n decideApproval(\n sessionId: string,\n requestId: string,\n decision: AiApprovalDecisionDto,\n ): Observable<void> {\n return this.http.post<void>(\n `${this.tenantBase()}/sessions/${encodeURIComponent(sessionId)}` +\n `/approvals/${encodeURIComponent(requestId)}`,\n decision,\n );\n }\n\n /**\n * `POST /{tenantId}/v1/credentials/tickets` — mint a one-time credential-\n * registration ticket (#4133). The admin sees the plaintext code once in\n * the response; the server stores only the SHA-256 hash. The matching\n * redemption call (`POST /v1/credentials/tickets/redeem`) is anonymous and\n * NOT in this client because the redeemer is by design a different person\n * on a different machine — they use the bastion CLI, not the Studio.\n */\n issueCredentialTicket(\n request: IssueAiCredentialTicketRequestDto,\n ): Observable<IssueAiCredentialTicketResponseDto> {\n return this.http.post<IssueAiCredentialTicketResponseDto>(\n `${this.tenantBase()}/credentials/tickets`,\n request,\n );\n }\n\n /**\n * `POST /{tenantId}/v1/credentials/github-pat` — register a GitHub Personal\n * Access Token (#4124). The adapter validates the token against GitHub's\n * `/user` endpoint before encryption; a refused token surfaces as HTTP 422\n * with `error: 'github_refused_token'`. The response is intentionally minimal\n * (`{ rtId }`); the host re-fetches the list so the rendering path stays\n * shared with refresh.\n */\n registerGitHubPat(\n request: RegisterAiGitHubPatRequestDto,\n ): Observable<{ rtId: string }> {\n return this.http.post<{ rtId: string }>(\n `${this.tenantBase()}/credentials/github-pat`,\n request,\n );\n }\n\n /**\n * `GET /{tenantId}/v1/credentials/github-pat` — list registered PATs with a\n * masked tail + GitHub login. Plaintext never crosses the wire.\n */\n listGitHubPats(): Observable<AiGitHubPatDto[]> {\n return this.http.get<AiGitHubPatDto[]>(\n `${this.tenantBase()}/credentials/github-pat`,\n );\n }\n\n /**\n * `DELETE /{tenantId}/v1/credentials/github-pat/{rtId}` — revoke a PAT. The\n * ciphertext is deleted, not soft-tombstoned. Returns void on 204.\n */\n deleteGitHubPat(rtId: string): Observable<void> {\n return this.http.delete<void>(\n `${this.tenantBase()}/credentials/github-pat/${encodeURIComponent(rtId)}`,\n );\n }\n\n /**\n * `GET /{tenantId}/v1/app-templates` — list the Custom-App templates the\n * adapter exposes for the wizard (#4145). Phase-1 returns the same three\n * static entries for every tenant; the wire shape is stable so per-tenant\n * overrides can ride it without an SPA bump.\n */\n getAppTemplates(): Observable<AiAppTemplateDto[]> {\n return this.http.get<AiAppTemplateDto[]>(\n `${this.tenantBase()}/app-templates`,\n );\n }\n\n private tenantBase(): string {\n return `${this.options.baseUrl}/${encodeURIComponent(this.options.tenantId)}/v1`;\n }\n}\n","import { Injectable, inject } from '@angular/core';\nimport { Observable, ReplaySubject, Subject } from 'rxjs';\nimport {\n AI_ADAPTER_OPTIONS,\n AI_SESSION_STREAM_CONNECTION_FACTORY,\n AiHubConnectionLike,\n} from './ai-adapter-options';\nimport { AiAdapterClientService } from './ai-adapter-client.service';\nimport {\n AiSessionEventDto,\n AiSessionStatusChangedDto,\n} from '../models/ai-session-event';\nimport {\n AiApprovalDecidedDto,\n AiApprovalRequestedDto,\n} from '../models/ai-approval';\nimport { AiQuotaWarningDto } from '../models/ai-quota';\n\n/**\n * One open SignalR connection's worth of streams. Sessions get one\n * `SessionStream` per `streamSession()` call; closing the returned subscription\n * tears the connection down.\n */\nexport interface AiSessionStream {\n /** Stream-json lines plus orchestrator markers, ordered by `sequence`. */\n readonly events$: Observable<AiSessionEventDto>;\n /** Lifecycle transitions for the session. */\n readonly statusChanges$: Observable<AiSessionStatusChangedDto>;\n /** Approval gate fired — UI opens the modal. */\n readonly approvalsRequested$: Observable<AiApprovalRequestedDto>;\n /** Approval resolved — UI dismisses the modal in every tab. */\n readonly approvalsDecided$: Observable<AiApprovalDecidedDto>;\n /** Tenant-wide quota threshold crossed — UI renders a banner. */\n readonly quotaWarnings$: Observable<AiQuotaWarningDto>;\n\n /** Tear down the underlying SignalR connection and complete all observables. */\n disconnect(): void;\n}\n\n/**\n * Internal wrapper around the SignalR connection. The library does not pin a\n * SignalR version — host applications inject a `HubConnectionBuilder` they\n * already use, which keeps the library agnostic of the\n * `@microsoft/signalr` major version line (six and seven have diverged on the\n * `withAutomaticReconnect()` defaults).\n *\n * For now the connection is constructed deferred — `streamSession` builds it on\n * demand so the host can defer the SignalR JS bundle behind a route lazy-load.\n * Phase-1 ships a stub backed by `ReplaySubject` so demo apps can drive\n * components without a live hub; the real connection wiring lives behind a\n * `connectionFactory` provider the host overrides.\n */\n/**\n * Not `providedIn: 'root'` — see <see cref=\"AiAdapterClientService\"/> for the\n * rationale: `AI_ADAPTER_OPTIONS` is a per-route token, the root injector\n * cannot resolve it. Element-level injection picks up the route-level provider.\n */\n@Injectable()\nexport class AiSessionStreamService {\n private readonly options = inject(AI_ADAPTER_OPTIONS);\n private readonly client = inject(AiAdapterClientService);\n private readonly connectionFactory = inject(\n AI_SESSION_STREAM_CONNECTION_FACTORY,\n { optional: true },\n );\n\n /**\n * Open a streaming session. Returns the per-stream subjects and a\n * `disconnect()` closer. When the host has provided an\n * {@link AI_SESSION_STREAM_CONNECTION_FACTORY}, a live SignalR connection is\n * built against {@link hubUrl} and the five hub callbacks\n * (`OnSessionEventAsync`, `OnSessionStatusChangedAsync`,\n * `OnApprovalRequestedAsync`, `OnApprovalDecidedAsync`, `OnQuotaWarningAsync`)\n * are routed into the matching subject. Without a factory, the service\n * degrades to REST-only backfill — the original Phase-1 stub contract.\n *\n * Tracking the highest sequence we've emitted lets the host reconnect after a\n * network blip by passing `sinceSequence` to {@link replay} (the SignalR\n * `onreconnected` hook fires that with the highest seen sequence).\n */\n streamSession(sessionId: string): AiSessionStream {\n const events$ = new ReplaySubject<AiSessionEventDto>(256);\n const statusChanges$ = new Subject<AiSessionStatusChangedDto>();\n const approvalsRequested$ = new Subject<AiApprovalRequestedDto>();\n const approvalsDecided$ = new Subject<AiApprovalDecidedDto>();\n const quotaWarnings$ = new Subject<AiQuotaWarningDto>();\n\n // Backfill any persisted events the hub-connect process would have missed\n // — REST returns the full history up to \"now\", and the live channel takes\n // it from there. The combination is at-least-once with deterministic\n // ordering courtesy of the persisted `sequence` field.\n let highestSequence = 0;\n const backfill = this.client.listEvents(sessionId).subscribe({\n next: (events) => {\n for (const event of events) {\n highestSequence = Math.max(highestSequence, event.sequence);\n events$.next(event);\n }\n },\n });\n\n let connection: AiHubConnectionLike | null = null;\n if (this.connectionFactory) {\n connection = this.connectionFactory(this.hubUrl);\n }\n\n if (connection) {\n const conn = connection;\n conn.on('OnSessionEventAsync', (payload) => {\n const event = payload as AiSessionEventDto;\n highestSequence = Math.max(highestSequence, event.sequence ?? 0);\n events$.next(event);\n });\n conn.on('OnSessionStatusChangedAsync', (payload) => {\n statusChanges$.next(payload as AiSessionStatusChangedDto);\n });\n conn.on('OnApprovalRequestedAsync', (payload) => {\n approvalsRequested$.next(payload as AiApprovalRequestedDto);\n });\n conn.on('OnApprovalDecidedAsync', (payload) => {\n approvalsDecided$.next(payload as AiApprovalDecidedDto);\n });\n conn.on('OnQuotaWarningAsync', (payload) => {\n quotaWarnings$.next(payload as AiQuotaWarningDto);\n });\n conn.onreconnected(() => {\n // Pull anything we missed during the disconnect window so the\n // events$ stream stays gap-free without the caller having to do\n // their own bookkeeping.\n this.replay(sessionId, highestSequence, events$);\n });\n\n // Start the connection, then ask the hub to scope this connection's\n // group membership to the session. Failures are intentionally swallowed\n // — backfill stays valid and the UI degrades to REST-only instead of\n // crashing.\n conn\n .start()\n .then(() => conn.invoke('SubscribeToSessionAsync', sessionId))\n .catch(() => {\n /* hub start / subscribe failed — REST backfill still serves the UI */\n });\n }\n\n return {\n events$: events$.asObservable(),\n statusChanges$: statusChanges$.asObservable(),\n approvalsRequested$: approvalsRequested$.asObservable(),\n approvalsDecided$: approvalsDecided$.asObservable(),\n quotaWarnings$: quotaWarnings$.asObservable(),\n disconnect: () => {\n backfill.unsubscribe();\n if (connection) {\n // Best-effort — `stop()` may reject during teardown; the subjects\n // get completed regardless so consumers see clean termination.\n connection.stop().catch(() => undefined);\n connection = null;\n }\n events$.complete();\n statusChanges$.complete();\n approvalsRequested$.complete();\n approvalsDecided$.complete();\n quotaWarnings$.complete();\n },\n };\n }\n\n /**\n * Backfill missing events from a known sequence number and push them into\n * the host's existing stream. Used by a SignalR `onreconnected` hook the\n * host wires up.\n */\n replay(\n sessionId: string,\n sinceSequence: number,\n sink: Subject<AiSessionEventDto> | ReplaySubject<AiSessionEventDto>,\n ): void {\n this.client.listEvents(sessionId, sinceSequence).subscribe({\n next: (events) => events.forEach((event) => sink.next(event)),\n });\n }\n\n /**\n * Tenant-scoped hub URL. Composition matches the AI Adapter's hub map —\n * `app.MapHub<AiHub>(\"/{tenantId:tenantId}/aiHub\")` — so the host's\n * `hubPath` is appended after the tenant segment. Host apps that mount the\n * hub at a different relative path override `hubPath` via\n * `AI_ADAPTER_OPTIONS`.\n */\n get hubUrl(): string {\n const trimmed = this.options.hubPath.startsWith('/')\n ? this.options.hubPath\n : `/${this.options.hubPath}`;\n return `${this.options.baseUrl}/${this.options.tenantId}${trimmed}`;\n }\n}\n","import {\n EnvironmentProviders,\n InjectionToken,\n makeEnvironmentProviders,\n} from '@angular/core';\nimport { AiAdapterClientService } from './ai-adapter-client.service';\nimport { AiSessionStreamService } from './ai-session-stream.service';\n\n/**\n * Configuration the host application supplies via {@link provideOctoAiConsole}.\n * Every field is required so the library never falls back to a hard-coded URL or\n * tenant id — host apps that wire the library through their own DI container can\n * surface placeholders explicitly instead.\n */\nexport interface AiAdapterOptions {\n /**\n * Absolute base URL of the AI Adapter REST API, e.g. `https://ai.acme.cloud`.\n * Must NOT include a trailing slash. The library appends\n * `/{tenantId}/v1/sessions...` itself.\n */\n readonly baseUrl: string;\n\n /**\n * Tenant whose sessions the host wants to surface. The library renders the\n * tenant id into every REST URL. Host apps that have multiple tenants must\n * compose the provider with the tenant id from their routing context.\n */\n readonly tenantId: string;\n\n /**\n * SignalR hub path, relative to the base URL, e.g. `/hubs/ai`. Used by\n * `AiSessionStreamService` to connect when a host calls\n * `streamSession(sessionId)`.\n */\n readonly hubPath: string;\n}\n\n/**\n * DI token the services in this library read at construction time.\n * Provided by {@link provideOctoAiConsole}.\n */\nexport const AI_ADAPTER_OPTIONS = new InjectionToken<AiAdapterOptions>(\n 'mm-octo-ai-console.adapter-options',\n);\n\n/**\n * Minimal subset of the `@microsoft/signalr` `HubConnection` surface the library\n * exercises. The interface stays in the public API so host apps can supply\n * either the real SignalR connection (Refinery Studio, bastion CLI), a test\n * mock, or no factory at all — in which case {@link AiSessionStreamService}\n * falls back to REST-only backfill (Phase-1 stub behaviour). Keeping the type\n * structural lets the library stay agnostic of the `@microsoft/signalr` major\n * version line the host has installed.\n */\nexport interface AiHubConnectionLike {\n on(methodName: string, handler: (payload: unknown) => void): void;\n start(): Promise<void>;\n stop(): Promise<void>;\n invoke<T = unknown>(methodName: string, ...args: unknown[]): Promise<T>;\n onreconnected(handler: (connectionId?: string) => void): void;\n}\n\n/**\n * Builds a hub connection against the resolved URL. Return null when streaming\n * is not wired in this deployment yet — the stream service then quietly skips\n * the live channel and behaves exactly like the Phase-1 stub.\n */\nexport type AiSessionStreamConnectionFactory =\n (hubUrl: string) => AiHubConnectionLike | null;\n\n/**\n * Optional DI token a host application provides to enable the live SignalR\n * channel on {@link AiSessionStreamService}. When absent, the service degrades\n * to REST-only backfill.\n */\nexport const AI_SESSION_STREAM_CONNECTION_FACTORY =\n new InjectionToken<AiSessionStreamConnectionFactory>(\n 'mm-octo-ai-console.session-stream-connection-factory',\n );\n\n/**\n * Composable provider that surfaces the library's adapter configuration to the\n * Angular DI container. Host applications call this once in `app.config.ts`:\n *\n * ```ts\n * providers: [\n * provideOctoAiConsole({\n * baseUrl: 'https://ai.acme.cloud',\n * tenantId: 'acme',\n * hubPath: '/hubs/ai',\n * }),\n * ]\n * ```\n */\nexport function provideOctoAiConsole(\n options: AiAdapterOptions,\n): EnvironmentProviders {\n // Provide AiAdapterClientService + AiSessionStreamService alongside the\n // options token. Both services drop `providedIn: 'root'` (6d0e7daf) because\n // the options they read are route-scoped — a host with multiple tenants\n // composes the provider with the right tenant id per route, and root-\n // singleton services would freeze the first route's options. Bundling the\n // services into the same provider chain means a host's `providers: [\n // provideOctoAiConsole({...}) ]` is enough; no manual listing required.\n return makeEnvironmentProviders([\n { provide: AI_ADAPTER_OPTIONS, useValue: options },\n AiAdapterClientService,\n AiSessionStreamService,\n ]);\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { AiSessionStatus } from '../../models/ai-session';\n\n/**\n * Status pill that mirrors the eight values `AiSessionStatus` carries on the\n * wire. The component is purely visual — it takes a status string in and emits\n * a CSS class + display label that the host's stylesheet can theme via the\n * `--mm-ai-status-*` custom properties.\n */\n@Component({\n selector: 'mm-ai-job-status-badge',\n imports: [],\n templateUrl: './ai-job-status-badge.component.html',\n styleUrl: './ai-job-status-badge.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AiJobStatusBadgeComponent {\n readonly status = input.required<AiSessionStatus>();\n\n protected readonly variant = computed(() => {\n switch (this.status()) {\n case 'Running':\n return 'running';\n case 'Queued':\n return 'queued';\n case 'Paused':\n return 'paused';\n case 'Completed':\n return 'completed';\n case 'Failed':\n case 'Cancelled':\n return 'failed';\n case 'QuotaBlocked':\n case 'RateLimited':\n return 'blocked';\n default:\n return 'queued';\n }\n });\n}\n","<span class=\"mm-ai-status-badge mm-ai-status-badge--{{ variant() }}\">\n {{ status() }}\n</span>\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n input,\n output,\n} from '@angular/core';\nimport { AiSessionDto, AiSessionStatus } from '../../models/ai-session';\nimport { AiJobStatusBadgeComponent } from '../ai-job-status-badge/ai-job-status-badge.component';\n\n/**\n * List view of an AI Adapter tenant's sessions. The host owns data fetching —\n * the component takes the array as an input signal and emits the rtId on\n * row click. Keeping it data-source-agnostic lets refinery wire its own\n * Apollo cache while a bastion CLI could pass static fixtures in a Storybook.\n *\n * Cancel and Delete are surfaced as inline per-row actions and emitted as\n * separate outputs — the host owns the confirmation dialog and the actual\n * REST call. The component only knows which actions are valid for which\n * status, so a Running session never offers Delete and a Completed session\n * never offers Cancel.\n */\n@Component({\n selector: 'mm-ai-session-list',\n imports: [AiJobStatusBadgeComponent],\n templateUrl: './ai-session-list.component.html',\n styleUrl: './ai-session-list.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AiSessionListComponent {\n readonly sessions = input.required<AiSessionDto[]>();\n readonly selectedSessionId = input<string | null>(null);\n\n readonly sessionSelected = output<string>();\n readonly cancelRequested = output<string>();\n readonly deleteRequested = output<string>();\n\n protected readonly orderedSessions = computed(() =>\n [...this.sessions()].sort((a, b) =>\n b.startedAt.localeCompare(a.startedAt),\n ),\n );\n\n /**\n * Terminal status set, kept in sync with the C# `AiAgentSessionService.IsTerminal`\n * predicate. Delete is gated to this set; Cancel is gated to its complement.\n * If the wire enum grows a new terminal kind, both predicates must be\n * updated here AND on the server.\n */\n private static readonly TERMINAL_STATUSES: ReadonlySet<AiSessionStatus> =\n new Set<AiSessionStatus>(['Completed', 'Failed', 'Cancelled']);\n\n protected canCancel(status: AiSessionStatus): boolean {\n return !AiSessionListComponent.TERMINAL_STATUSES.has(status);\n }\n\n protected canDelete(status: AiSessionStatus): boolean {\n return AiSessionListComponent.TERMINAL_STATUSES.has(status);\n }\n\n protected onSelect(sessionRtId: string): void {\n this.sessionSelected.emit(sessionRtId);\n }\n\n protected onCancel(sessionRtId: string, event: MouseEvent): void {\n // Without stopPropagation the row click also fires and selects this\n // session, which then redirects focus and tears the inline-action button\n // out from under the user mid-confirmation.\n event.stopPropagation();\n this.cancelRequested.emit(sessionRtId);\n }\n\n protected onDelete(sessionRtId: string, event: MouseEvent): void {\n event.stopPropagation();\n this.deleteRequested.emit(sessionRtId);\n }\n\n protected age(startedAt: string): string {\n const started = Date.parse(startedAt);\n if (Number.isNaN(started)) {\n return '';\n }\n const now = Date.now();\n const seconds = Math.floor((now - started) / 1000);\n if (seconds < 60) {\n return `${seconds}s ago`;\n }\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) {\n return `${minutes}m ago`;\n }\n const hours = Math.floor(minutes / 60);\n if (hours < 24) {\n return `${hours}h ago`;\n }\n const days = Math.floor(hours / 24);\n return `${days}d ago`;\n }\n}\n","<ul class=\"mm-ai-session-list\">\n @for (session of orderedSessions(); track session.sessionRtId) {\n <li\n class=\"mm-ai-session-list__row\"\n [class.mm-ai-session-list__row--selected]=\"\n session.sessionRtId === selectedSessionId()\n \"\n >\n <button\n type=\"button\"\n class=\"mm-ai-session-list__button\"\n (click)=\"onSelect(session.sessionRtId)\"\n >\n <!--\n Row layout: goal + status badge on top, meta + actions on the\n bottom. The two-line shape lets a narrow sidebar (Refinery\n Studio caps the column at 18rem) breathe — the previous\n horizontal cram had the goal text overlap the action buttons\n on every row.\n -->\n <div class=\"mm-ai-session-list__top\">\n <span class=\"mm-ai-session-list__goal\">{{ session.goalSummary }}</span>\n <mm-ai-job-status-badge [status]=\"session.status\" />\n </div>\n <div class=\"mm-ai-session-list__bottom\">\n <span class=\"mm-ai-session-list__meta\">\n {{ age(session.startedAt) }}\n <span class=\"mm-ai-session-list__dot\">·</span>\n {{ session.tokensConsumed }} tok\n </span>\n @if (session.previewUrl) {\n <!--\n #4136 — preview-deploy URL the server surfaces when the\n worker pod's nginx sidecar has a build to serve. Opens in\n a new tab so the sidebar scroll position survives.\n stopPropagation prevents the row click from also firing.\n -->\n <a\n class=\"mm-ai-session-list__preview\"\n [href]=\"session.previewUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n (click)=\"$event.stopPropagation()\"\n >\n Preview\n </a>\n }\n <span class=\"mm-ai-session-list__spacer\"></span>\n <span class=\"mm-ai-session-list__actions\">\n @if (canCancel(session.status)) {\n <button\n type=\"button\"\n class=\"mm-ai-session-list__action mm-ai-session-list__action--cancel\"\n [attr.aria-label]=\"'Cancel session ' + session.goalSummary\"\n (click)=\"onCancel(session.sessionRtId, $event)\"\n >\n Cancel\n </button>\n }\n @if (canDelete(session.status)) {\n <button\n type=\"button\"\n class=\"mm-ai-session-list__action mm-ai-session-list__action--delete\"\n [attr.aria-label]=\"'Delete session ' + session.goalSummary\"\n (click)=\"onDelete(session.sessionRtId, $event)\"\n >\n Delete\n </button>\n }\n </span>\n </div>\n </button>\n </li>\n } @empty {\n <li class=\"mm-ai-session-list__empty\">No sessions yet.</li>\n }\n</ul>\n","import { AiSessionEventDto } from '../../models/ai-session-event';\n\n/**\n * Token-usage summary extracted from an `assistant` or `result` payload.\n * Fields beyond `input` / `output` are optional because the upstream worker\n * (claude-code stream-json) sometimes omits them on rate-limited or\n * cache-only turns.\n */\nexport interface ParsedTokenUsage {\n readonly input: number;\n readonly output: number;\n readonly cacheRead?: number;\n readonly cacheCreation?: number;\n}\n\n/**\n * A single content fragment inside an assistant event. The worker emits a\n * mix of `text` and `tool_use` parts per message — sometimes one of each in\n * the same payload — and the renderer needs to walk them in order to keep\n * the conversational flow intact (text → \"I'll run X\" → tool_use card).\n */\nexport type ParsedAssistantPart =\n | { readonly type: 'text'; readonly text: string }\n | {\n readonly type: 'tool_use';\n readonly id: string;\n readonly toolName: string;\n readonly input: unknown;\n };\n\n/**\n * Discriminated union the chat-stream renders on. The component switches over\n * `kind` and pulls only the fields it needs per branch — no `any` walks of\n * the original payload at render time.\n *\n * `raw` is kept on every variant so a debug-view (\"show me the original\n * stream-json\") can surface it on demand without re-parsing.\n */\nexport type ParsedView =\n | {\n readonly kind: 'system';\n readonly subKind: 'init' | 'hook' | 'rate-limit' | 'other';\n readonly summary: string;\n readonly raw: unknown;\n }\n | {\n readonly kind: 'assistant';\n readonly parts: readonly ParsedAssistantPart[];\n readonly model: string | null;\n readonly usage: ParsedTokenUsage | null;\n readonly raw: unknown;\n }\n | {\n readonly kind: 'tool-result';\n readonly toolUseId: string;\n readonly content: string;\n readonly isError: boolean;\n readonly raw: unknown;\n }\n | {\n readonly kind: 'result';\n readonly text: string;\n readonly durationMs: number;\n readonly usage: ParsedTokenUsage | null;\n readonly costUsd: number | null;\n readonly reason: string;\n readonly isError: boolean;\n readonly raw: unknown;\n }\n | {\n readonly kind: 'status-change';\n readonly status: string;\n readonly raw: unknown;\n }\n | {\n readonly kind: 'unknown';\n readonly summary: string;\n readonly raw: unknown;\n };\n\n/**\n * Result of one parse step — wraps the source event so the renderer can still\n * carry `sequence` / `at` / `actorRef` through to the template.\n */\nexport interface ParsedEvent {\n readonly event: AiSessionEventDto;\n readonly view: ParsedView;\n}\n\n/**\n * Parse one persisted session event into a `ParsedView`. Falls back to\n * `unknown` (never throws) when the payload is malformed — we'd rather show\n * \"unrecognised event\" than crash the whole transcript.\n */\nexport function parseSessionEvent(event: AiSessionEventDto): ParsedEvent {\n const payload = safeJsonParse(event.payload);\n if (payload === parseFailure) {\n return {\n event,\n view: {\n kind: 'unknown',\n summary: 'Malformed event payload',\n raw: event.payload,\n },\n };\n }\n\n if (event.kind === 'StatusChange' && isRecord(payload)) {\n // The orchestrator emits a StatusChange wrapper around any non-content\n // worker event — system/init, hooks, rate-limit, etc. Demote based on the\n // wrapped `type` so the renderer can collapse the noise.\n if (isClaudeStreamFrame(payload)) {\n return { event, view: viewFromClaudeFrame(payload) };\n }\n const status = readString(payload, 'newStatus') ?? readString(payload, 'status');\n if (status) {\n return { event, view: { kind: 'status-change', status, raw: payload } };\n }\n }\n\n if (event.kind === 'Hook' && isRecord(payload)) {\n return { event, view: viewSystemHook(payload) };\n }\n\n if (event.kind === 'Message' && isRecord(payload)) {\n if (isClaudeStreamFrame(payload)) {\n return { event, view: viewFromClaudeFrame(payload) };\n }\n // Bare text Message — promote to a single-part assistant bubble so the\n // visual treatment is consistent.\n const text = readString(payload, 'text');\n if (text) {\n return {\n event,\n view: {\n kind: 'assistant',\n parts: [{ type: 'text', text }],\n model: null,\n usage: null,\n raw: payload,\n },\n };\n }\n }\n\n if (event.kind === 'ToolCall' && isRecord(payload)) {\n return {\n event,\n view: {\n kind: 'assistant',\n parts: [\n {\n type: 'tool_use',\n id: readString(payload, 'callId') ?? '',\n toolName: readString(payload, 'toolName') ?? 'tool',\n input: payload['arguments'] ?? null,\n },\n ],\n model: null,\n usage: null,\n raw: payload,\n },\n };\n }\n\n if (event.kind === 'ToolResult' && isRecord(payload)) {\n return {\n event,\n view: {\n kind: 'tool-result',\n toolUseId: readString(payload, 'callId') ?? readString(payload, 'toolUseId') ?? '',\n content: stringifyToolContent(payload['result'] ?? payload['content']),\n isError: readBoolean(payload, 'isError') ?? false,\n raw: payload,\n },\n };\n }\n\n if (event.kind === 'Error') {\n return {\n event,\n view: {\n kind: 'unknown',\n summary: typeof payload === 'string' ? payload : 'Error event',\n raw: payload,\n },\n };\n }\n\n return {\n event,\n view: { kind: 'unknown', summary: `Unhandled kind: ${event.kind}`, raw: payload },\n };\n}\n\n/**\n * Sort parsed events by sequence so out-of-order arrival (the REST backfill +\n * live SignalR union) renders deterministically. Kept here so the component\n * doesn't redo the sort logic every render cycle.\n */\nexport function parseAndOrder(events: readonly AiSessionEventDto[]): ParsedEvent[] {\n return [...events]\n .sort((a, b) => a.sequence - b.sequence)\n .map(parseSessionEvent);\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Internals\n// ────────────────────────────────────────────────────────────────────────────\n\nconst parseFailure = Symbol('parseFailure');\n\nfunction safeJsonParse(raw: string): unknown | typeof parseFailure {\n try {\n return JSON.parse(raw);\n } catch {\n return parseFailure;\n }\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nfunction readString(rec: Record<string, unknown>, key: string): string | null {\n const v = rec[key];\n return typeof v === 'string' && v.length > 0 ? v : null;\n}\n\nfunction readNumber(rec: Record<string, unknown>, key: string): number | null {\n const v = rec[key];\n return typeof v === 'number' && Number.isFinite(v) ? v : null;\n}\n\nfunction readBoolean(rec: Record<string, unknown>, key: string): boolean | null {\n const v = rec[key];\n return typeof v === 'boolean' ? v : null;\n}\n\n/**\n * Claude Code's stream-json shapes a frame as `{ type, ... }` with a small\n * vocabulary: `system`, `assistant`, `user`, `result`, `rate_limit_event`.\n * We branch on `type` (+ optional `subtype` for `system`) and rely on every\n * other claim being optional — the renderer must not crash on a frame we've\n * never seen.\n */\nfunction isClaudeStreamFrame(rec: Record<string, unknown>): boolean {\n return typeof rec['type'] === 'string';\n}\n\nfunction viewFromClaudeFrame(rec: Record<string, unknown>): ParsedView {\n const type = rec['type'];\n\n if (type === 'system') {\n const subtype = readString(rec, 'subtype');\n if (subtype === 'init') {\n return {\n kind: 'system',\n subKind: 'init',\n summary: `Worker initialised — ${readString(rec, 'model') ?? 'model'}`,\n raw: rec,\n };\n }\n if (subtype === 'hook_started' || subtype === 'hook_response') {\n const hookName = readString(rec, 'hook_name') ?? 'hook';\n return {\n kind: 'system',\n subKind: 'hook',\n summary:\n subtype === 'hook_started'\n ? `Hook started — ${hookName}`\n : `Hook completed — ${hookName}`,\n raw: rec,\n };\n }\n return {\n kind: 'system',\n subKind: 'other',\n summary: `System event${subtype ? ' — ' + subtype : ''}`,\n raw: rec,\n };\n }\n\n if (type === 'rate_limit_event') {\n const info = rec['rate_limit_info'];\n let summary = 'Rate-limit event';\n if (isRecord(info)) {\n const status = readString(info, 'status');\n const rateLimitType = readString(info, 'rateLimitType');\n if (status && rateLimitType) {\n summary = `Rate-limit ${status} (${rateLimitType})`;\n }\n }\n return { kind: 'system', subKind: 'rate-limit', summary, raw: rec };\n }\n\n if (type === 'assistant') {\n const message = rec['message'];\n if (isRecord(message)) {\n const parts = extractAssistantParts(message['content']);\n const model = readString(message, 'model');\n const usage = isRecord(message['usage']) ? extractUsage(message['usage']) : null;\n return {\n kind: 'assistant',\n parts,\n model,\n usage,\n raw: rec,\n };\n }\n }\n\n if (type === 'user') {\n // claude wraps tool_result inside a `user` frame's content. Extract the\n // first tool_result we find; if there isn't one, fall through to unknown.\n const message = rec['message'];\n if (isRecord(message) && Array.isArray(message['content'])) {\n for (const part of message['content']) {\n if (isRecord(part) && part['type'] === 'tool_result') {\n const rawContent = part['content'];\n return {\n kind: 'tool-result',\n toolUseId: readString(part, 'tool_use_id') ?? '',\n content: stringifyToolContent(rawContent),\n isError: readBoolean(part, 'is_error') ?? false,\n raw: rec,\n };\n }\n }\n }\n }\n\n if (type === 'result') {\n const usage = isRecord(rec['usage']) ? extractUsage(rec['usage']) : null;\n return {\n kind: 'result',\n text: readString(rec, 'result') ?? '',\n durationMs: readNumber(rec, 'duration_ms') ?? 0,\n usage,\n costUsd: readNumber(rec, 'total_cost_usd'),\n reason: readString(rec, 'terminal_reason') ?? readString(rec, 'stop_reason') ?? 'completed',\n isError: readBoolean(rec, 'is_error') ?? false,\n raw: rec,\n };\n }\n\n return {\n kind: 'unknown',\n summary: `Unrecognised claude frame type: ${typeof type === 'string' ? type : 'n/a'}`,\n raw: rec,\n };\n}\n\nfunction viewSystemHook(rec: Record<string, unknown>): ParsedView {\n // Hooks come through as a `Hook` event-kind on our side but the payload\n // shape matches claude's hook_started / hook_response stream-json.\n return viewFromClaudeFrame(rec);\n}\n\nfunction extractAssistantParts(content: unknown): readonly ParsedAssistantPart[] {\n if (!Array.isArray(content)) {\n return [];\n }\n const parts: ParsedAssistantPart[] = [];\n for (const part of content) {\n if (!isRecord(part)) {\n continue;\n }\n if (part['type'] === 'text') {\n const text = readString(part, 'text');\n if (text !== null) {\n parts.push({ type: 'text', text });\n }\n } else if (part['type'] === 'tool_use') {\n parts.push({\n type: 'tool_use',\n id: readString(part, 'id') ?? '',\n toolName: readString(part, 'name') ?? 'tool',\n input: part['input'] ?? null,\n });\n }\n }\n return parts;\n}\n\nfunction extractUsage(usage: Record<string, unknown>): ParsedTokenUsage {\n return {\n input: readNumber(usage, 'input_tokens') ?? 0,\n output: readNumber(usage, 'output_tokens') ?? 0,\n cacheRead: readNumber(usage, 'cache_read_input_tokens') ?? undefined,\n cacheCreation: readNumber(usage, 'cache_creation_input_tokens') ?? undefined,\n };\n}\n\n/**\n * Tool result `content` comes in many shapes: a plain string, an array of\n * `{type:'text', text:...}` blocks (the Anthropic content-block format), or\n * raw JSON. Flatten everything we recognise into a single string for the\n * UI; the consumer can still pop the raw payload for inspection.\n */\nfunction stringifyToolContent(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (Array.isArray(content)) {\n return content\n .map((block) => {\n if (isRecord(block) && block['type'] === 'text') {\n return readString(block, 'text') ?? '';\n }\n return JSON.stringify(block);\n })\n .join('\\n');\n }\n if (content === undefined || content === null) {\n return '';\n }\n try {\n return JSON.stringify(content, null, 2);\n } catch {\n return String(content);\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n input,\n signal,\n} from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { AiSessionEventDto } from '../../models/ai-session-event';\nimport { ParsedEvent, parseAndOrder } from './event-parser';\n\n/**\n * Read-only transcript of a session, derived from the events stream.\n *\n * The component takes raw `AiSessionEventDto` entries, runs them through the\n * `event-parser` to produce a typed `ParsedView` per event, and renders each\n * kind with its own treatment — assistant bubbles for chat text, expandable\n * cards for tool calls + tool results, single-line collapsed strips for the\n * system / hook noise, and a stats panel for the terminal `result` summary.\n *\n * Markdown rendering inside assistant text is still deferred (concept §10);\n * for now the parser hands plain text to the template and the SCSS preserves\n * line breaks via `white-space: pre-wrap`. Picking a renderer (markdown-it vs\n * marked) is gated on a CSP review and a markdown-corpus regression test.\n */\n@Component({\n selector: 'mm-ai-chat-stream',\n standalone: true,\n imports: [CommonModule],\n templateUrl: './ai-chat-stream.component.html',\n styleUrl: './ai-chat-stream.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AiChatStreamComponent {\n readonly events = input.required<AiSessionEventDto[]>();\n\n /**\n * Sorted + parsed. Recomputes whenever the input signal changes; the sort\n * is stable so live appends from SignalR keep prior render positions.\n */\n protected readonly parsedEvents = computed<ParsedEvent[]>(() =>\n parseAndOrder(this.events()),\n );\n\n /**\n * Per-event \"show raw JSON\" toggle. Keyed by sequence so live appends\n * don't flicker existing toggles closed. A `Set<number>` rather than a\n * record so add / delete avoids `keyof` shenanigans.\n */\n private readonly expandedSet = signal<ReadonlySet<number>>(new Set());\n\n protected isExpanded(sequence: number): boolean {\n return this.expandedSet().has(sequence);\n }\n\n protected toggleRaw(sequence: number): void {\n this.expandedSet.update((prev) => {\n const next = new Set(prev);\n if (next.has(sequence)) {\n next.delete(sequence);\n } else {\n next.add(sequence);\n }\n return next;\n });\n }\n\n /**\n * `HH:MM:SS` formatter — full ISO is too noisy in a 30-event transcript.\n * Returns the input unchanged if parsing fails so we don't pretend events\n * we can't read are recent.\n */\n protected formatTime(iso: string): string {\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) {\n return iso;\n }\n return d.toLocaleTimeString('de-AT', { hour12: false });\n }\n\n /** Compact \"1234 tok\" for the assistant header. */\n protected formatTokenCount(n: number): string {\n if (n >= 1000) {\n return `${(n / 1000).toFixed(1)} ktok`;\n }\n return `${n} tok`;\n }\n\n /** Duration in either `ms` (sub-second) or `s.SS`. */\n protected formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)} ms`;\n }\n return `${(ms / 1000).toFixed(2)} s`;\n }\n\n /** Cost in EUR-style decimals; null + zero collapse to a dash. */\n protected formatCost(usd: number | null | undefined): string {\n if (usd == null || usd === 0) {\n return '—';\n }\n return `$${usd.toFixed(4)}`;\n }\n\n /** Pretty-print arbitrary tool_use input or raw blocks for the expandable detail panel. */\n protected formatRaw(value: unknown): string {\n if (value === undefined || value === null) {\n return '';\n }\n if (typeof value === 'string') {\n return value;\n }\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n }\n}\n","<div class=\"mm-ai-chat\">\n @for (parsed of parsedEvents(); track parsed.event.sequence) {\n @switch (parsed.view.kind) {\n @case ('assistant') {\n <article class=\"mm-ai-chat__bubble mm-ai-chat__bubble--assistant\">\n <header class=\"mm-ai-chat__bubble-header\">\n <span class=\"mm-ai-chat__role\">Assistant</span>\n @if (parsed.view.model; as model) {\n <span class=\"mm-ai-chat__meta\">{{ model }}</span>\n }\n @if (parsed.view.usage; as usage) {\n <span class=\"mm-ai-chat__meta\">\n {{ formatTokenCount(usage.input + usage.output) }}\n </span>\n }\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </header>\n @for (part of parsed.view.parts; track $index) {\n @switch (part.type) {\n @case ('text') {\n <p class=\"mm-ai-chat__text\">{{ part.text }}</p>\n }\n @case ('tool_use') {\n <div class=\"mm-ai-chat__tool-use\">\n <header class=\"mm-ai-chat__tool-use-header\">\n <span class=\"mm-ai-chat__tool-badge\">tool</span>\n <span class=\"mm-ai-chat__tool-name\">{{ part.toolName }}</span>\n </header>\n <pre class=\"mm-ai-chat__tool-input\">{{ formatRaw(part.input) }}</pre>\n </div>\n }\n }\n }\n </article>\n }\n\n @case ('tool-result') {\n <article\n class=\"mm-ai-chat__tool-result\"\n [class.mm-ai-chat__tool-result--error]=\"parsed.view.isError\"\n >\n <header class=\"mm-ai-chat__tool-result-header\">\n <span class=\"mm-ai-chat__role\">Tool result</span>\n @if (parsed.view.isError) {\n <span class=\"mm-ai-chat__error-badge\">error</span>\n }\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </header>\n <pre class=\"mm-ai-chat__tool-result-body\">{{ parsed.view.content }}</pre>\n </article>\n }\n\n @case ('result') {\n <article class=\"mm-ai-chat__result\"\n [class.mm-ai-chat__result--error]=\"parsed.view.isError\">\n <header class=\"mm-ai-chat__result-header\">\n <span class=\"mm-ai-chat__role\">{{ parsed.view.reason }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </header>\n @if (parsed.view.text) {\n <p class=\"mm-ai-chat__result-text\">{{ parsed.view.text }}</p>\n }\n <dl class=\"mm-ai-chat__result-stats\">\n <div>\n <dt>Duration</dt>\n <dd>{{ formatDuration(parsed.view.durationMs) }}</dd>\n </div>\n <div>\n <dt>Tokens (in/out)</dt>\n <dd>\n @if (parsed.view.usage) {\n {{ formatTokenCount(parsed.view.usage.input) }}\n /\n {{ formatTokenCount(parsed.view.usage.output) }}\n } @else {\n —\n }\n </dd>\n </div>\n <div>\n <dt>Cost</dt>\n <dd>{{ formatCost(parsed.view.costUsd) }}</dd>\n </div>\n </dl>\n </article>\n }\n\n @case ('system') {\n <button\n type=\"button\"\n class=\"mm-ai-chat__system\"\n [class.mm-ai-chat__system--init]=\"parsed.view.subKind === 'init'\"\n [class.mm-ai-chat__system--hook]=\"parsed.view.subKind === 'hook'\"\n [class.mm-ai-chat__system--rate]=\"parsed.view.subKind === 'rate-limit'\"\n [attr.aria-expanded]=\"isExpanded(parsed.event.sequence)\"\n (click)=\"toggleRaw(parsed.event.sequence)\"\n >\n <span class=\"mm-ai-chat__system-chevron\">\n {{ isExpanded(parsed.event.sequence) ? '▾' : '▸' }}\n </span>\n <span class=\"mm-ai-chat__system-summary\">{{ parsed.view.summary }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </button>\n @if (isExpanded(parsed.event.sequence)) {\n <pre class=\"mm-ai-chat__system-detail\">{{ formatRaw(parsed.view.raw) }}</pre>\n }\n }\n\n @case ('status-change') {\n <div class=\"mm-ai-chat__status\">\n <span class=\"mm-ai-chat__status-label\">Status</span>\n <span class=\"mm-ai-chat__status-value\">{{ parsed.view.status }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </div>\n }\n\n @case ('unknown') {\n <button\n type=\"button\"\n class=\"mm-ai-chat__system mm-ai-chat__system--unknown\"\n [attr.aria-expanded]=\"isExpanded(parsed.event.sequence)\"\n (click)=\"toggleRaw(parsed.event.sequence)\"\n >\n <span class=\"mm-ai-chat__system-chevron\">\n {{ isExpanded(parsed.event.sequence) ? '▾' : '▸' }}\n </span>\n <span class=\"mm-ai-chat__system-summary\">{{ parsed.view.summary }}</span>\n <time class=\"mm-ai-chat__time\">{{ formatTime(parsed.event.at) }}</time>\n </button>\n @if (isExpanded(parsed.event.sequence)) {\n <pre class=\"mm-ai-chat__system-detail\">{{ formatRaw(parsed.view.raw) }}</pre>\n }\n }\n }\n } @empty {\n <div class=\"mm-ai-chat__empty\">\n No events yet — the worker hasn't emitted anything.\n </div>\n }\n</div>\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n input,\n signal,\n} from '@angular/core';\nimport { AiToolCallDto } from '../../models/ai-session-event';\n\n/**\n * Expandable card surface for one tool invocation in a session transcript.\n * Click the header to toggle arguments + result; the body is rendered as\n * pretty-printed JSON so the host doesn't need its own json formatter.\n */\n@Component({\n selector: 'mm-ai-tool-call',\n imports: [],\n templateUrl: './ai-tool-call.component.html',\n styleUrl: './ai-tool-call.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AiToolCallComponent {\n readonly call = input.required<AiToolCallDto>();\n\n protected readonly expanded = signal(false);\n\n protected readonly statusVariant = computed(() => {\n const status = this.call().status;\n switch (status) {\n case 'Succeeded':\n return 'succeeded';\n case 'Failed':\n return 'failed';\n case 'Rejected':\n return 'rejected';\n case 'Approved':\n case 'Pending':\n return 'pending';\n default:\n return 'pending';\n }\n });\n\n protected readonly argumentsJson = computed(() =>\n this.format(this.call().arguments),\n );\n protected readonly resultJson = computed(() => {\n const result = this.call().result;\n return result === undefined ? null : this.format(result);\n });\n\n protected toggleExpanded(): void {\n this.expanded.update((v) => !v);\n }\n\n private format(value: unknown): string {\n if (value === null || value === undefined) {\n return '';\n }\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n }\n}\n","<div class=\"mm-ai-tool-call mm-ai-tool-call--{{ statusVariant() }}\">\n <button\n type=\"button\"\n class=\"mm-ai-tool-call__header\"\n (click)=\"toggleExpanded()\"\n [attr.aria-expanded]=\"expanded()\"\n >\n <span class=\"mm-ai-tool-call__chevron\">\n {{ expanded() ? '▾' : '▸' }}\n </span>\n <span class=\"mm-ai-tool-call__name\">{{ call().toolName }}</span>\n <span class=\"mm-ai-tool-call__status\">{{ call().status }}</span>\n @if (call().durationMs != null) {\n <span class=\"mm-ai-tool-call__duration\">\n {{ call().durationMs }}ms\n </span>\n }\n </button>\n\n @if (expanded()) {\n <div class=\"mm-ai-tool-call__body\">\n <div class=\"mm-ai-tool-call__section\">\n <h4 class=\"mm-ai-tool-call__heading\">Arguments</h4>\n <pre class=\"mm-ai-tool-call__code\">{{ argumentsJson() }}</pre>\n </div>\n\n @if (resultJson() !== null) {\n <div class=\"mm-ai-tool-call__section\">\n <h4 class=\"mm-ai-tool-call__heading\">Result</h4>\n <pre class=\"mm-ai-tool-call__code\">{{ resultJson() }}</pre>\n </div>\n }\n </div>\n }\n</div>\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport {\n AiApprovalDecisionDto,\n AiApprovalRequestedDto,\n} from '../../models/ai-approval';\n\n/**\n * Approval modal. The host opens it when a `AiApprovalRequestedDto` arrives on\n * the SignalR stream, fills the `request` input, and listens for the `decide`\n * output to call `AiAdapterClientService.decideApproval`. The modal itself is\n * presentation-only: no networking, no global state, no portal magic.\n *\n * The host is expected to wrap the modal in its own backdrop / focus-trap so\n * the library doesn't pull in a positioning dependency (CDK overlay, Kendo\n * window, ng-bootstrap, …) — each host shop already has one.\n */\n@Component({\n selector: 'mm-ai-approval-modal',\n imports: [FormsModule],\n templateUrl: './ai-approval-modal.component.html',\n styleUrl: './ai-approval-modal.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AiApprovalModalComponent {\n readonly request = input.required<AiApprovalRequestedDto>();\n\n readonly decide = output<AiApprovalDecisionDto>();\n readonly dismiss = output<void>();\n\n protected readonly comment = signal('');\n\n protected readonly prettyPayload = computed(() => {\n try {\n return JSON.stringify(JSON.parse(this.request().payload), null, 2);\n } catch {\n return this.request().payload;\n }\n });\n\n protected approve(): void {\n this.decide.emit({\n outcome: 'Approved',\n comment: this.commentOrUndefined(),\n });\n }\n\n protected reject(): void {\n this.decide.emit({\n outcome: 'Rejected',\n comment: this.commentOrUndefined(),\n });\n }\n\n protected onDismiss(): void {\n this.dismiss.emit();\n }\n\n private commentOrUndefined(): string | undefined {\n const trimmed = this.comment().trim();\n return trimmed.length > 0 ? trimmed : undefined;\n }\n}\n","<div class=\"mm-ai-approval\">\n <header class=\"mm-ai-approval__header\">\n <h3 class=\"mm-ai-approval__title\">Approve tool call</h3>\n <button\n type=\"button\"\n class=\"mm-ai-approval__close\"\n (click)=\"onDismiss()\"\n aria-label=\"Dismiss\"\n >\n ×\n </button>\n </header>\n\n <div class=\"mm-ai-approval__meta\">\n <div><span>Tool:</span> <code>{{ request().toolName }}</code></div>\n <div><span>Reason:</span> <code>{{ request().reason }}</code></div>\n <div><span>Session:</span> <code>{{ request().sessionId }}</code></div>\n </div>\n\n <div class=\"mm-ai-approval__section\">\n <h4 class=\"mm-ai-approval__heading\">Payload</h4>\n <pre class=\"mm-ai-approval__code\">{{ prettyPayload() }}</pre>\n </div>\n\n <div class=\"mm-ai-approval__section\">\n <h4 class=\"mm-ai-approval__heading\">Comment <span>(optional)</span></h4>\n <textarea\n class=\"mm-ai-approval__comment\"\n [ngModel]=\"comment()\"\n (ngModelChange)=\"comment.set($event)\"\n rows=\"3\"\n placeholder=\"Why is this approved or rejected?\"\n ></textarea>\n </div>\n\n <footer class=\"mm-ai-approval__actions\">\n <button\n type=\"button\"\n class=\"mm-ai-approval__btn mm-ai-approval__btn--reject\"\n (click)=\"reject()\"\n >\n Reject\n </button>\n <button\n type=\"button\"\n class=\"mm-ai-approval__btn mm-ai-approval__btn--approve\"\n (click)=\"approve()\"\n >\n Approve\n </button>\n </footer>\n</div>\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\n\n/**\n * Two-bar usage indicator (daily + monthly) with threshold colour state.\n * Inputs are unit-agnostic counts and caps; the component does not parse the\n * tenant's `AiQuotaLimit` itself — the host is expected to feed numbers the\n * adapter already aggregated.\n */\n@Component({\n selector: 'mm-ai-quota-indicator',\n imports: [],\n templateUrl: './ai-quota-indicator.component.html',\n styleUrl: './ai-quota-indicator.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AiQuotaIndicatorComponent {\n readonly dailyConsumed = input.required<number>();\n readonly dailyCap = input.required<number>();\n readonly monthlyConsumed = input<number | null>(null);\n readonly monthlyCap = input<number | null>(null);\n\n protected readonly dailyPercent = computed(() =>\n this.percent(this.dailyConsumed(), this.dailyCap()),\n );\n protected readonly monthlyPercent = computed(() => {\n const consumed = this.monthlyConsumed();\n const cap = this.monthlyCap();\n if (consumed == null || cap == null) {\n return null;\n }\n return this.percent(consumed, cap);\n });\n\n protected readonly dailyState = computed(() => this.toState(this.dailyPercent()));\n protected readonly monthlyState = computed(() => {\n const pct = this.monthlyPercent();\n return pct == null ? null : this.toState(pct);\n });\n\n private percent(consumed: number, cap: number): number {\n if (cap <= 0) {\n return 0;\n }\n return Math.min(100, Math.round((consumed / cap) * 100));\n }\n\n private toState(percent: number): 'ok' | 'warning' | 'critical' {\n if (percent >= 100) {\n return 'critical';\n }\n if (percent >= 80) {\n return 'warning';\n }\n return 'ok';\n }\n}\n","<div class=\"mm-ai-quota\">\n <div class=\"mm-ai-quota__row\">\n <span class=\"mm-ai-quota__label\">Daily</span>\n <div class=\"mm-ai-quota__bar mm-ai-quota__bar--{{ dailyState() }}\">\n <div class=\"mm-ai-quota__fill\" [style.width.%]=\"dailyPercent()\"></div>\n </div>\n <span class=\"mm-ai-quota__count\">\n {{ dailyConsumed() }} / {{ dailyCap() }} ({{ dailyPercent() }}%)\n </span>\n </div>\n\n @if (monthlyPercent() !== null) {\n <div class=\"mm-ai-quota__row\">\n <span class=\"mm-ai-quota__label\">Monthly</span>\n <div class=\"mm-ai-quota__bar mm-ai-quota__bar--{{ monthlyState() }}\">\n <div class=\"mm-ai-quota__fill\" [style.width.%]=\"monthlyPercent()\"></div>\n </div>\n <span class=\"mm-ai-quota__count\">\n {{ monthlyConsumed() }} / {{ monthlyCap() }} ({{ monthlyPercent() }}%)\n </span>\n </div>\n }\n</div>\n","import { CommonModule } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n inject,\n signal,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { AiAdapterClientService } from '../../services/ai-adapter-client.service';\nimport {\n AiCredentialTicketScope,\n IssueAiCredentialTicketResponseDto,\n} from '../../models/ai-credential-ticket';\n\n/**\n * Self-service credential-ticket issuer (#4133, concept §10). Admin picks a\n * scope + TTL, hits **Issue ticket**, copies the displayed code, hands it to\n * a developer / operator out-of-band. The plaintext code is shown exactly\n * once — refreshing the panel after issue clears it so a careless screenshot\n * doesn't leak.\n *\n * Theme-neutral by design: visuals use CSS custom properties with neutral\n * defaults so the host (Refinery Studio LCARS, demo-app, etc.) can override\n * them via `--mm-ai-ticket-*` variables without touching the component.\n */\n@Component({\n selector: 'mm-ai-credential-ticket-issue',\n imports: [CommonModule, FormsModule],\n templateUrl: './ai-credential-ticket-issue.component.html',\n styleUrl: './ai-credential-ticket-issue.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AiCredentialTicketIssueComponent {\n private readonly client = inject(AiAdapterClientService);\n\n protected readonly scope = signal<AiCredentialTicketScope>(\n 'CredentialRegister',\n );\n protected readonly ttlMinutes = signal<number>(5);\n\n protected readonly issuing = signal<boolean>(false);\n protected readonly issued = signal<IssueAiCredentialTicketResponseDto | null>(\n null,\n );\n protected readonly error = signal<string | null>(null);\n protected readonly copied = signal<boolean>(false);\n\n /** Available scope choices — kept in sync with `AiCredentialTicketScope`. */\n protected readonly scopes: readonly {\n value: AiCredentialTicketScope;\n label: string;\n hint: string;\n }[] = [\n {\n value: 'CredentialRegister',\n label: 'Subscription token',\n hint: 'For the bastion CLI to register an Anthropic OAuth pair on this tenant.',\n },\n {\n value: 'DevSshKeyRegister',\n label: 'Developer SSH key',\n hint: 'For a developer to register their own SSH public key on this tenant.',\n },\n ];\n\n protected onIssue(): void {\n if (this.issuing()) {\n return;\n }\n const ttl = this.normalizedTtl();\n this.issuing.set(true);\n this.error.set(null);\n this.copied.set(false);\n this.issued.set(null);\n\n this.client\n .issueCredentialTicket({\n scope: this.scope(),\n ttlMinutes: ttl,\n })\n .subscribe({\n next: (response) => {\n this.issued.set(response);\n this.issuing.set(false);\n },\n error: (err: unknown) => {\n this.error.set(this.formatError(err));\n this.issuing.set(false);\n },\n });\n }\n\n protected onCopy(): void {\n const code = this.issued()?.code;\n if (!code) {\n return;\n }\n // navigator.clipboard requires a secure context; if it's unavailable the\n // operator can still select + copy by hand. Falling back silently keeps\n // the panel usable on http://localhost dev origins.\n if (navigator.clipboard && navigator.clipboard.writeText) {\n navigator.clipboard.writeText(code).then(\n () => this.copied.set(true),\n () => this.copied.set(false),\n );\n }\n }\n\n protected onClear(): void {\n this.issued.set(null);\n this.copied.set(false);\n }\n\n private normalizedTtl(): number {\n const v = Math.trunc(this.ttlMinutes());\n // Server clamps to 60 anyway; clamp here too so the slider can't push a\n // value the request would silently truncate.\n if (Number.isNaN(v) || v < 1) {\n return 1;\n }\n if (v > 60) {\n return 60;\n }\n return v;\n }\n\n private formatError(err: unknown): string {\n if (\n typeof err === 'object' &&\n err !== null &&\n 'status' in err &&\n typeof (err as { status: number }).status === 'number'\n ) {\n const status = (err as { status: number }).status;\n if (status === 401 || status === 403) {\n return 'Not authorised — only tenant admins can issue tickets.';\n }\n if (status === 400) {\n return 'Invalid request — check the scope value.';\n }\n return `Server returned HTTP ${status}.`;\n }\n return 'Failed to reach the AI Adapter — check the network.';\n }\n}\n","<section class=\"mm-ai-ticket\">\n <header class=\"mm-ai-ticket__header\">\n <div class=\"mm-ai-ticket__title\">Issue credential ticket</div>\n <div class=\"mm-ai-ticket__subtitle\">\n Mint a one-time code an operator redeems on their own machine.\n </div>\n </header>\n\n @if (!issued()) {\n <form\n class=\"mm-ai-ticket__form\"\n (ngSubmit)=\"onIssue()\"\n #issueForm=\"ngForm\"\n >\n <label class=\"mm-ai-ticket__field\">\n <span class=\"mm-ai-ticket__label\">Scope</span>\n <select\n class=\"mm-ai-ticket__select\"\n [ngModel]=\"scope()\"\n (ngModelChange)=\"scope.set($event)\"\n name=\"scope\"\n [disabled]=\"issuing()\"\n >\n @for (s of scopes; track s.value) {\n <option [value]=\"s.value\">{{ s.label }}</option>\n }\n </select>\n </label>\n <p class=\"mm-ai-ticket__hint\">\n @for (s of scopes; track s.value) {\n @if (s.value === scope()) {\n {{ s.hint }}\n }\n }\n </p>\n\n <label class=\"mm-ai-ticket__field\">\n <span class=\"mm-ai-ticket__label\">TTL (minutes)</span>\n <input\n class=\"mm-ai-ticket__input\"\n type=\"number\"\n min=\"1\"\n max=\"60\"\n step=\"1\"\n [ngModel]=\"ttlMinutes()\"\n (ngModelChange)=\"ttlMinutes.set(+$event)\"\n name=\"ttlMinutes\"\n [disabled]=\"issuing()\"\n />\n </label>\n <p class=\"mm-ai-ticket__hint\">\n Default 5 minutes. Server clamps to 60 — long TTLs leak.\n </p>\n\n <div class=\"mm-ai-ticket__actions\">\n <button\n class=\"mm-ai-ticket__button mm-ai-ticket__button--primary\"\n type=\"submit\"\n [disabled]=\"issuing()\"\n >\n @if (issuing()) {\n Issuing…\n } @else {\n Issue ticket\n }\n </button>\n </div>\n\n @if (error()) {\n <p class=\"mm-ai-ticket__error\">{{ error() }}</p>\n }\n </form>\n } @else {\n <div class=\"mm-ai-ticket__result\">\n <p class=\"mm-ai-ticket__result-label\">\n One-time code (copy now — not shown again):\n </p>\n <div class=\"mm-ai-ticket__code-row\">\n <code class=\"mm-ai-ticket__code\">{{ issued()!.code }}</code>\n <button\n class=\"mm-ai-ticket__button mm-ai-ticket__button--secondary\"\n type=\"button\"\n (click)=\"onCopy()\"\n >\n @if (copied()) {\n Copied\n } @else {\n Copy\n }\n </button>\n </div>\n <dl class=\"mm-ai-ticket__meta\">\n <div>\n <dt>Scope</dt>\n <dd>{{ issued()!.scope }}</dd>\n </div>\n <div>\n <dt>Expires at</dt>\n <dd>{{ issued()!.expiresAt }}</dd>\n </div>\n <div>\n <dt>Ticket rtId</dt>\n <dd><code>{{ issued()!.rtId }}</code></dd>\n </div>\n </dl>\n <div class=\"mm-ai-ticket__actions\">\n <button\n class=\"mm-ai-ticket__button\"\n type=\"button\"\n (click)=\"onClear()\"\n >\n Issue another\n </button>\n </div>\n </div>\n }\n</section>\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { AiAppTemplateDto, AiNewAppSubmission } from '../../models/ai-app-template';\n\n/**\n * \"New App from Template\" wizard (#4145). Presentation-only — the host fetches\n * the template catalogue via {@link AiAdapterClientService.getAppTemplates},\n * feeds it into the `templates` input, and listens for `submit` / `cancel` to\n * call `createSession`.\n *\n * The component is intentionally framework-agnostic (no Kendo, no CDK overlay)\n * so consumers can wrap it in whatever modal shell they already have — same\n * convention as `AiApprovalModalComponent`.\n *\n * The interpolation engine is *deliberately* dumb: a regex substitution over\n * `{{placeholder}}` tokens. The wizard only collects two inputs (project name +\n * primary entity), and a full Handlebars dependency for two tokens would be\n * overkill. If a future template needs richer rendering, switch this to\n * `handlebars.precompile`-at-build-time and re-evaluate.\n */\n@Component({\n selector: 'mm-ai-new-app-dialog',\n imports: [FormsModule],\n templateUrl: './ai-new-app-dialog.component.html',\n styleUrl: './ai-new-app-dialog.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AiNewAppDialogComponent {\n /** Available templates from the adapter — typically 3 in Phase 1. */\n readonly templates = input.required<readonly AiAppTemplateDto[]>();\n\n /** Tenant name shown in the dialog header so the operator confirms the scope. */\n readonly tenantName = input<string>('');\n\n /**\n * Emitted on Submit with the fully interpolated Goal + the picked template id.\n * Named `confirmed` (not `submit`) because Angular flags native-event collisions\n * — `submit` is a DOM event and triggers `@angular-eslint/no-output-native`.\n */\n readonly confirmed = output<AiNewAppSubmission>();\n\n /**\n * Emitted on Cancel / dismiss. The host hides the wizard on this signal.\n * Named `cancelled` for the same reason `confirmed` is not `submit`.\n */\n readonly cancelled = output<void>();\n\n /** rtId-style id of the picked template, or empty until the operator picks one. */\n protected readonly selectedTemplateId = signal('');\n protected readonly projectName = signal('');\n protected readonly primaryEntity = signal('');\n\n protected readonly selectedTemplate = computed<AiAppTemplateDto | null>(() => {\n const id = this.selectedTemplateId();\n return this.templates().find((t) => t.id === id) ?? null;\n });\n\n /**\n * Rendered Goal string the operator sees in the preview block. Stays empty\n * until a template is picked AND every required placeholder has a value, so\n * the operator never sees a half-interpolated string.\n */\n protected readonly renderedGoal = computed(() => {\n const template = this.selectedTemplate();\n if (!template) {\n return '';\n }\n if (!this.allRequiredFilled()) {\n return '';\n }\n return this.interpolate(template.goalTemplate);\n });\n\n /** True when every placeholder the picked template declares has a non-empty value. */\n protected readonly allRequiredFilled = computed(() => {\n const template = this.selectedTemplate();\n if (!template) {\n return false;\n }\n return template.requiredPlaceholders.every((key) => {\n const value = this.valueFor(key);\n return value !== null && value.trim().length > 0;\n });\n });\n\n protected onSubmit(): void {\n const template = this.selectedTemplate();\n if (!template || !this.allRequiredFilled()) {\n return;\n }\n this.confirmed.emit({\n templateId: template.id,\n jobKind: template.jobKind,\n projectName: this.projectName().trim(),\n primaryEntity: this.primaryEntity().trim(),\n goal: this.interpolate(template.goalTemplate),\n });\n }\n\n protected onCancel(): void {\n this.cancelled.emit();\n }\n\n /** Read the signal that backs the named placeholder, or `null` if unknown. */\n private valueFor(placeholder: string): string | null {\n switch (placeholder) {\n case 'projectName':\n return this.projectName();\n case 'primaryEntity':\n return this.primaryEntity();\n default:\n return null;\n }\n }\n\n private interpolate(template: string): string {\n return template.replace(/{{\\s*(\\w+)\\s*}}/g, (full, key) => {\n const value = this.valueFor(key);\n // Unknown placeholders survive verbatim so a typo in the catalogue\n // surfaces in the preview rather than silently disappearing.\n return value !== null ? value.trim() : full;\n });\n }\n}\n","<div class=\"mm-ai-new-app\">\n <header class=\"mm-ai-new-app__header\">\n <h3 class=\"mm-ai-new-app__title\">\n New App\n @if (tenantName(); as tenant) {\n <span class=\"mm-ai-new-app__tenant\"> · {{ tenant }}</span>\n }\n </h3>\n <button\n type=\"button\"\n class=\"mm-ai-new-app__close\"\n (click)=\"onCancel()\"\n aria-label=\"Dismiss\"\n >\n ×\n </button>\n </header>\n\n <div class=\"mm-ai-new-app__section\">\n <h4 class=\"mm-ai-new-app__heading\">Template</h4>\n <ul class=\"mm-ai-new-app__templates\" role=\"radiogroup\" aria-label=\"Template\">\n @for (template of templates(); track template.id) {\n <li>\n <label\n class=\"mm-ai-new-app__template\"\n [class.mm-ai-new-app__template--active]=\"\n selectedTemplateId() === template.id\n \"\n >\n <input\n type=\"radio\"\n name=\"mm-ai-new-app-template\"\n [value]=\"template.id\"\n [checked]=\"selectedTemplateId() === template.id\"\n (change)=\"selectedTemplateId.set(template.id)\"\n />\n <span class=\"mm-ai-new-app__template-body\">\n <strong class=\"mm-ai-new-app__template-name\">{{ template.name }}</strong>\n <span class=\"mm-ai-new-app__template-description\">\n {{ template.description }}\n </span>\n </span>\n </label>\n </li>\n }\n </ul>\n </div>\n\n @if (selectedTemplate(); as template) {\n <div class=\"mm-ai-new-app__section\">\n <h4 class=\"mm-ai-new-app__heading\">Parameters</h4>\n <div class=\"mm-ai-new-app__field\">\n <label for=\"mm-ai-new-app-project-name\">Project name</label>\n <input\n id=\"mm-ai-new-app-project-name\"\n type=\"text\"\n [ngModel]=\"projectName()\"\n (ngModelChange)=\"projectName.set($event)\"\n placeholder=\"e.g. customer-list\"\n autocomplete=\"off\"\n />\n </div>\n <div class=\"mm-ai-new-app__field\">\n <label for=\"mm-ai-new-app-primary-entity\">Primary entity (CK type)</label>\n <input\n id=\"mm-ai-new-app-primary-entity\"\n type=\"text\"\n [ngModel]=\"primaryEntity()\"\n (ngModelChange)=\"primaryEntity.set($event)\"\n placeholder=\"e.g. Customer\"\n autocomplete=\"off\"\n />\n </div>\n </div>\n\n <div class=\"mm-ai-new-app__section\">\n <h4 class=\"mm-ai-new-app__heading\">\n Generated Goal <span>(this is what Claude will see)</span>\n </h4>\n <pre class=\"mm-ai-new-app__preview\">{{\n renderedGoal() ||\n 'Fill in every field above to preview the Goal that will be sent.'\n }}</pre>\n </div>\n }\n\n <footer class=\"mm-ai-new-app__actions\">\n <button\n type=\"button\"\n class=\"mm-ai-new-app__btn mm-ai-new-app__btn--cancel\"\n (click)=\"onCancel()\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mm-ai-new-app__btn mm-ai-new-app__btn--submit\"\n [disabled]=\"!allRequiredFilled()\"\n (click)=\"onSubmit()\"\n >\n Create App Session\n </button>\n </footer>\n</div>\n","/*\n * Public API Surface of @meshmakers/octo-ai-console\n *\n * Reusable Angular building blocks for the OctoMesh AI Adapter session console\n * (ADR-21, issue #4121). Host applications import individual standalone\n * components or call `provideOctoAiConsole({...})` to wire the services.\n */\n\nexport * from './lib/models';\nexport * from './lib/services';\nexport * from './lib/components';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;;AAqBA;;;;;;;;;;;AAWG;AACH;;;;;;;AAOG;MAEU,sBAAsB,CAAA;AAChB,IAAA,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC;AACzB,IAAA,OAAO,GAAG,MAAM,CAAC,kBAAkB,CAAC;AAErD;;;AAGG;AACH,IAAA,YAAY,CAAC,MAAe,EAAA;QAC1B,MAAM,GAAG,GAAG,CAAA,EAAG,IAAI,CAAC,UAAU,EAAE,WAAW;AAC3C,QAAA,IAAI,MAAM,GAAG,IAAI,UAAU,EAAE;QAC7B,IAAI,MAAM,EAAE;YACV,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC;QACvC;AACA,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAiB,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC;IACvD;;AAGA,IAAA,UAAU,CAAC,SAAiB,EAAA;AAC1B,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAClB,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,kBAAkB,CAAC,SAAS,CAAC,CAAA,CAAE,CACjE;IACH;AAEA;;;;AAIG;AACH,IAAA,aAAa,CACX,OAAgC,EAAA;AAEhC,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CACnB,CAAA,EAAG,IAAI,CAAC,UAAU,EAAE,CAAA,SAAA,CAAW,EAC/B,OAAO,CACR;IACH;AAEA;;;;AAIG;AACH,IAAA,aAAa,CAAC,SAAiB,EAAA;QAC7B,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CACnB,CAAA,EAAG,IAAI,CAAC,UAAU,EAAE,CAAA,UAAA,EAAa,kBAAkB,CAAC,SAAS,CAAC,SAAS,EACvE,IAAI,CACL;IACH;AAEA;;;;;;AAMG;AACH,IAAA,aAAa,CAAC,SAAiB,EAAA;AAC7B,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CACrB,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,kBAAkB,CAAC,SAAS,CAAC,CAAA,CAAE,CACjE;IACH;AAEA;;;;;;;;;;;;;;;AAeG;IACH,WAAW,CAAC,SAAiB,EAAE,IAAY,EAAA;QACzC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CACnB,CAAA,EAAG,IAAI,CAAC,UAAU,EAAE,aAAa,kBAAkB,CAAC,SAAS,CAAC,CAAA,SAAA,CAAW,EACzE,EAAE,IAAI,EAAE,CACT;IACH;AAEA;;;;;AAKG;IACH,UAAU,CACR,SAAiB,EACjB,aAAsB,EAAA;AAEtB,QAAA,MAAM,GAAG,GAAG,CAAA,EAAG,IAAI,CAAC,UAAU,EAAE,CAAA,UAAA,EAAa,kBAAkB,CAAC,SAAS,CAAC,SAAS;AACnF,QAAA,IAAI,MAAM,GAAG,IAAI,UAAU,EAAE;AAC7B,QAAA,IAAI,aAAa,IAAI,IAAI,EAAE;AACzB,YAAA,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,eAAe,EAAE,aAAa,CAAC,QAAQ,EAAE,CAAC;QAChE;AACA,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAsB,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC;IAC5D;AAEA;;;;AAIG;AACH,IAAA,cAAc,CACZ,SAAiB,EACjB,SAAiB,EACjB,QAA+B,EAAA;AAE/B,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CACnB,CAAA,EAAG,IAAI,CAAC,UAAU,EAAE,CAAA,UAAA,EAAa,kBAAkB,CAAC,SAAS,CAAC,CAAA,CAAE;YAC9D,CAAA,WAAA,EAAc,kBAAkB,CAAC,SAAS,CAAC,EAAE,EAC/C,QAAQ,CACT;IACH;AAEA;;;;;;;AAOG;AACH,IAAA,qBAAqB,CACnB,OAA0C,EAAA;AAE1C,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CACnB,CAAA,EAAG,IAAI,CAAC,UAAU,EAAE,CAAA,oBAAA,CAAsB,EAC1C,OAAO,CACR;IACH;AAEA;;;;;;;AAOG;AACH,IAAA,iBAAiB,CACf,OAAsC,EAAA;AAEtC,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CACnB,CAAA,EAAG,IAAI,CAAC,UAAU,EAAE,CAAA,uBAAA,CAAyB,EAC7C,OAAO,CACR;IACH;AAEA;;;AAGG;IACH,cAAc,GAAA;AACZ,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAClB,CAAA,EAAG,IAAI,CAAC,UAAU,EAAE,CAAA,uBAAA,CAAyB,CAC9C;IACH;AAEA;;;AAGG;AACH,IAAA,eAAe,CAAC,IAAY,EAAA;AAC1B,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CACrB,GAAG,IAAI,CAAC,UAAU,EAAE,2BAA2B,kBAAkB,CAAC,IAAI,CAAC,CAAA,CAAE,CAC1E;IACH;AAEA;;;;;AAKG;IACH,eAAe,GAAA;AACb,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAClB,CAAA,EAAG,IAAI,CAAC,UAAU,EAAE,CAAA,cAAA,CAAgB,CACrC;IACH;IAEQ,UAAU,GAAA;AAChB,QAAA,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAA,CAAA,EAAI,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK;IAClF;wGA7LW,sBAAsB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;4GAAtB,sBAAsB,EAAA,CAAA;;4FAAtB,sBAAsB,EAAA,UAAA,EAAA,CAAA;kBADlC;;;ACFD;;;;;;;;;;;;AAYG;AACH;;;;AAIG;MAEU,sBAAsB,CAAA;AAChB,IAAA,OAAO,GAAG,MAAM,CAAC,kBAAkB,CAAC;AACpC,IAAA,MAAM,GAAG,MAAM,CAAC,sBAAsB,CAAC;IACvC,iBAAiB,GAAG,MAAM,CACzC,oCAAoC,EACpC,EAAE,QAAQ,EAAE,IAAI,EAAE,CACnB;AAED;;;;;;;;;;;;;AAaG;AACH,IAAA,aAAa,CAAC,SAAiB,EAAA;AAC7B,QAAA,MAAM,OAAO,GAAG,IAAI,aAAa,CAAoB,GAAG,CAAC;AACzD,QAAA,MAAM,cAAc,GAAG,IAAI,OAAO,EAA6B;AAC/D,QAAA,MAAM,mBAAmB,GAAG,IAAI,OAAO,EAA0B;AACjE,QAAA,MAAM,iBAAiB,GAAG,IAAI,OAAO,EAAwB;AAC7D,QAAA,MAAM,cAAc,GAAG,IAAI,OAAO,EAAqB;;;;;QAMvD,IAAI,eAAe,GAAG,CAAC;AACvB,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC;AAC3D,YAAA,IAAI,EAAE,CAAC,MAAM,KAAI;AACf,gBAAA,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;oBAC1B,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,KAAK,CAAC,QAAQ,CAAC;AAC3D,oBAAA,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;gBACrB;YACF,CAAC;AACF,SAAA,CAAC;QAEF,IAAI,UAAU,GAA+B,IAAI;AACjD,QAAA,IAAI,IAAI,CAAC,iBAAiB,EAAE;YAC1B,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC;QAClD;QAEA,IAAI,UAAU,EAAE;YACd,MAAM,IAAI,GAAG,UAAU;YACvB,IAAI,CAAC,EAAE,CAAC,qBAAqB,EAAE,CAAC,OAAO,KAAI;gBACzC,MAAM,KAAK,GAAG,OAA4B;AAC1C,gBAAA,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC;AAChE,gBAAA,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;AACrB,YAAA,CAAC,CAAC;YACF,IAAI,CAAC,EAAE,CAAC,6BAA6B,EAAE,CAAC,OAAO,KAAI;AACjD,gBAAA,cAAc,CAAC,IAAI,CAAC,OAAoC,CAAC;AAC3D,YAAA,CAAC,CAAC;YACF,IAAI,CAAC,EAAE,CAAC,0BAA0B,EAAE,CAAC,OAAO,KAAI;AAC9C,gBAAA,mBAAmB,CAAC,IAAI,CAAC,OAAiC,CAAC;AAC7D,YAAA,CAAC,CAAC;YACF,IAAI,CAAC,EAAE,CAAC,wBAAwB,EAAE,CAAC,OAAO,KAAI;AAC5C,gBAAA,iBAAiB,CAAC,IAAI,CAAC,OAA+B,CAAC;AACzD,YAAA,CAAC,CAAC;YACF,IAAI,CAAC,EAAE,CAAC,qBAAqB,EAAE,CAAC,OAAO,KAAI;AACzC,gBAAA,cAAc,CAAC,IAAI,CAAC,OAA4B,CAAC;AACnD,YAAA,CAAC,CAAC;AACF,YAAA,IAAI,CAAC,aAAa,CAAC,MAAK;;;;gBAItB,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,eAAe,EAAE,OAAO,CAAC;AAClD,YAAA,CAAC,CAAC;;;;;YAMF;AACG,iBAAA,KAAK;AACL,iBAAA,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,yBAAyB,EAAE,SAAS,CAAC;iBAC5D,KAAK,CAAC,MAAK;;AAEZ,YAAA,CAAC,CAAC;QACN;QAEA,OAAO;AACL,YAAA,OAAO,EAAE,OAAO,CAAC,YAAY,EAAE;AAC/B,YAAA,cAAc,EAAE,cAAc,CAAC,YAAY,EAAE;AAC7C,YAAA,mBAAmB,EAAE,mBAAmB,CAAC,YAAY,EAAE;AACvD,YAAA,iBAAiB,EAAE,iBAAiB,CAAC,YAAY,EAAE;AACnD,YAAA,cAAc,EAAE,cAAc,CAAC,YAAY,EAAE;YAC7C,UAAU,EAAE,MAAK;gBACf,QAAQ,CAAC,WAAW,EAAE;gBACtB,IAAI,UAAU,EAAE;;;oBAGd,UAAU,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM,SAAS,CAAC;oBACxC,UAAU,GAAG,IAAI;gBACnB;gBACA,OAAO,CAAC,QAAQ,EAAE;gBAClB,cAAc,CAAC,QAAQ,EAAE;gBACzB,mBAAmB,CAAC,QAAQ,EAAE;gBAC9B,iBAAiB,CAAC,QAAQ,EAAE;gBAC5B,cAAc,CAAC,QAAQ,EAAE;YAC3B,CAAC;SACF;IACH;AAEA;;;;AAIG;AACH,IAAA,MAAM,CACJ,SAAiB,EACjB,aAAqB,EACrB,IAAmE,EAAA;QAEnE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,SAAS,CAAC;YACzD,IAAI,EAAE,CAAC,MAAM,KAAK,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC9D,SAAA,CAAC;IACJ;AAEA;;;;;;AAMG;AACH,IAAA,IAAI,MAAM,GAAA;QACR,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG;AACjD,cAAE,IAAI,CAAC,OAAO,CAAC;cACb,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;AAC9B,QAAA,OAAO,CAAA,EAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAA,CAAA,EAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAA,EAAG,OAAO,EAAE;IACrE;wGAxIW,sBAAsB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;4GAAtB,sBAAsB,EAAA,CAAA;;4FAAtB,sBAAsB,EAAA,UAAA,EAAA,CAAA;kBADlC;;;ACpBD;;;AAGG;MACU,kBAAkB,GAAG,IAAI,cAAc,CAClD,oCAAoC;AA4BtC;;;;AAIG;MACU,oCAAoC,GAC/C,IAAI,cAAc,CAChB,sDAAsD;AAG1D;;;;;;;;;;;;;AAaG;AACG,SAAU,oBAAoB,CAClC,OAAyB,EAAA;;;;;;;;AASzB,IAAA,OAAO,wBAAwB,CAAC;AAC9B,QAAA,EAAE,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,OAAO,EAAE;QAClD,sBAAsB;QACtB,sBAAsB;AACvB,KAAA,CAAC;AACJ;;AC1GA;;;;;AAKG;MAQU,yBAAyB,CAAA;AAC3B,IAAA,MAAM,GAAG,KAAK,CAAC,QAAQ,4EAAmB;AAEhC,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAK;AACzC,QAAA,QAAQ,IAAI,CAAC,MAAM,EAAE;AACnB,YAAA,KAAK,SAAS;AACZ,gBAAA,OAAO,SAAS;AAClB,YAAA,KAAK,QAAQ;AACX,gBAAA,OAAO,QAAQ;AACjB,YAAA,KAAK,QAAQ;AACX,gBAAA,OAAO,QAAQ;AACjB,YAAA,KAAK,WAAW;AACd,gBAAA,OAAO,WAAW;AACpB,YAAA,KAAK,QAAQ;AACb,YAAA,KAAK,WAAW;AACd,gBAAA,OAAO,QAAQ;AACjB,YAAA,KAAK,cAAc;AACnB,YAAA,KAAK,aAAa;AAChB,gBAAA,OAAO,SAAS;AAClB,YAAA;AACE,gBAAA,OAAO,QAAQ;;AAErB,IAAA,CAAC,8EAAC;wGAtBS,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAzB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,yBAAyB,wNChBtC,sGAGA,EAAA,MAAA,EAAA,CAAA,+tCAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FDaa,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBAPrC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,wBAAwB,EAAA,OAAA,EACzB,EAAE,EAAA,eAAA,EAGM,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,sGAAA,EAAA,MAAA,EAAA,CAAA,+tCAAA,CAAA,EAAA;;;AEJjD;;;;;;;;;;;AAWG;MAQU,sBAAsB,CAAA;AACxB,IAAA,QAAQ,GAAG,KAAK,CAAC,QAAQ,8EAAkB;AAC3C,IAAA,iBAAiB,GAAG,KAAK,CAAgB,IAAI,wFAAC;IAE9C,eAAe,GAAG,MAAM,EAAU;IAClC,eAAe,GAAG,MAAM,EAAU;IAClC,eAAe,GAAG,MAAM,EAAU;AAExB,IAAA,eAAe,GAAG,QAAQ,CAAC,MAC5C,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAC7B,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CACvC,sFACF;AAED;;;;;AAKG;AACK,IAAA,OAAgB,iBAAiB,GACvC,IAAI,GAAG,CAAkB,CAAC,WAAW,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AAEtD,IAAA,SAAS,CAAC,MAAuB,EAAA;QACzC,OAAO,CAAC,sBAAsB,CAAC,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC;IAC9D;AAEU,IAAA,SAAS,CAAC,MAAuB,EAAA;QACzC,OAAO,sBAAsB,CAAC,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC;IAC7D;AAEU,IAAA,QAAQ,CAAC,WAAmB,EAAA;AACpC,QAAA,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC;IACxC;IAEU,QAAQ,CAAC,WAAmB,EAAE,KAAiB,EAAA;;;;QAIvD,KAAK,CAAC,eAAe,EAAE;AACvB,QAAA,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC;IACxC;IAEU,QAAQ,CAAC,WAAmB,EAAE,KAAiB,EAAA;QACvD,KAAK,CAAC,eAAe,EAAE;AACvB,QAAA,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC;IACxC;AAEU,IAAA,GAAG,CAAC,SAAiB,EAAA;QAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;AACrC,QAAA,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE;AACzB,YAAA,OAAO,EAAE;QACX;AACA,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;AACtB,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,OAAO,IAAI,IAAI,CAAC;AAClD,QAAA,IAAI,OAAO,GAAG,EAAE,EAAE;YAChB,OAAO,CAAA,EAAG,OAAO,CAAA,KAAA,CAAO;QAC1B;QACA,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;AACxC,QAAA,IAAI,OAAO,GAAG,EAAE,EAAE;YAChB,OAAO,CAAA,EAAG,OAAO,CAAA,KAAA,CAAO;QAC1B;QACA,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;AACtC,QAAA,IAAI,KAAK,GAAG,EAAE,EAAE;YACd,OAAO,CAAA,EAAG,KAAK,CAAA,KAAA,CAAO;QACxB;QACA,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC;QACnC,OAAO,CAAA,EAAG,IAAI,CAAA,KAAA,CAAO;IACvB;wGApEW,sBAAsB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;4FAAtB,sBAAsB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,EAAA,iBAAA,EAAA,mBAAA,EAAA,UAAA,EAAA,mBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,eAAA,EAAA,iBAAA,EAAA,eAAA,EAAA,iBAAA,EAAA,eAAA,EAAA,iBAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EC7BnC,g+FA6EA,EAAA,MAAA,EAAA,CAAA,42FAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDrDY,yBAAyB,EAAA,QAAA,EAAA,wBAAA,EAAA,MAAA,EAAA,CAAA,QAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAKxB,sBAAsB,EAAA,UAAA,EAAA,CAAA;kBAPlC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,oBAAoB,WACrB,CAAC,yBAAyB,CAAC,EAAA,eAAA,EAGnB,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,g+FAAA,EAAA,MAAA,EAAA,CAAA,42FAAA,CAAA,EAAA;;;AE8DjD;;;;AAIG;AACG,SAAU,iBAAiB,CAAC,KAAwB,EAAA;IACxD,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC;AAC5C,IAAA,IAAI,OAAO,KAAK,YAAY,EAAE;QAC5B,OAAO;YACL,KAAK;AACL,YAAA,IAAI,EAAE;AACJ,gBAAA,IAAI,EAAE,SAAS;AACf,gBAAA,OAAO,EAAE,yBAAyB;gBAClC,GAAG,EAAE,KAAK,CAAC,OAAO;AACnB,aAAA;SACF;IACH;IAEA,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE;;;;AAItD,QAAA,IAAI,mBAAmB,CAAC,OAAO,CAAC,EAAE;YAChC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,mBAAmB,CAAC,OAAO,CAAC,EAAE;QACtD;AACA,QAAA,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC;QAChF,IAAI,MAAM,EAAE;AACV,YAAA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;QACzE;IACF;IAEA,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE;QAC9C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE;IACjD;IAEA,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE;AACjD,QAAA,IAAI,mBAAmB,CAAC,OAAO,CAAC,EAAE;YAChC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,mBAAmB,CAAC,OAAO,CAAC,EAAE;QACtD;;;QAGA,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC;QACxC,IAAI,IAAI,EAAE;YACR,OAAO;gBACL,KAAK;AACL,gBAAA,IAAI,EAAE;AACJ,oBAAA,IAAI,EAAE,WAAW;oBACjB,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC/B,oBAAA,KAAK,EAAE,IAAI;AACX,oBAAA,KAAK,EAAE,IAAI;AACX,oBAAA,GAAG,EAAE,OAAO;AACb,iBAAA;aACF;QACH;IACF;IAEA,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE;QAClD,OAAO;YACL,KAAK;AACL,YAAA,IAAI,EAAE;AACJ,gBAAA,IAAI,EAAE,WAAW;AACjB,gBAAA,KAAK,EAAE;AACL,oBAAA;AACE,wBAAA,IAAI,EAAE,UAAU;wBAChB,EAAE,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,EAAE;wBACvC,QAAQ,EAAE,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,MAAM;AACnD,wBAAA,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,IAAI,IAAI;AACpC,qBAAA;AACF,iBAAA;AACD,gBAAA,KAAK,EAAE,IAAI;AACX,gBAAA,KAAK,EAAE,IAAI;AACX,gBAAA,GAAG,EAAE,OAAO;AACb,aAAA;SACF;IACH;IAEA,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE;QACpD,OAAO;YACL,KAAK;AACL,YAAA,IAAI,EAAE;AACJ,gBAAA,IAAI,EAAE,aAAa;AACnB,gBAAA,SAAS,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,IAAI,EAAE;AAClF,gBAAA,OAAO,EAAE,oBAAoB,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,SAAS,CAAC,CAAC;gBACtE,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,KAAK;AACjD,gBAAA,GAAG,EAAE,OAAO;AACb,aAAA;SACF;IACH;AAEA,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE;QAC1B,OAAO;YACL,KAAK;AACL,YAAA,IAAI,EAAE;AACJ,gBAAA,IAAI,EAAE,SAAS;AACf,gBAAA,OAAO,EAAE,OAAO,OAAO,KAAK,QAAQ,GAAG,OAAO,GAAG,aAAa;AAC9D,gBAAA,GAAG,EAAE,OAAO;AACb,aAAA;SACF;IACH;IAEA,OAAO;QACL,KAAK;AACL,QAAA,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAA,gBAAA,EAAmB,KAAK,CAAC,IAAI,CAAA,CAAE,EAAE,GAAG,EAAE,OAAO,EAAE;KAClF;AACH;AAEA;;;;AAIG;AACG,SAAU,aAAa,CAAC,MAAoC,EAAA;IAChE,OAAO,CAAC,GAAG,MAAM;AACd,SAAA,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ;SACtC,GAAG,CAAC,iBAAiB,CAAC;AAC3B;AAEA;AACA;AACA;AAEA,MAAM,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC;AAE3C,SAAS,aAAa,CAAC,GAAW,EAAA;AAChC,IAAA,IAAI;AACF,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;IACxB;AAAE,IAAA,MAAM;AACN,QAAA,OAAO,YAAY;IACrB;AACF;AAEA,SAAS,QAAQ,CAAC,CAAU,EAAA;AAC1B,IAAA,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;AACjE;AAEA,SAAS,UAAU,CAAC,GAA4B,EAAE,GAAW,EAAA;AAC3D,IAAA,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC;AAClB,IAAA,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI;AACzD;AAEA,SAAS,UAAU,CAAC,GAA4B,EAAE,GAAW,EAAA;AAC3D,IAAA,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC;AAClB,IAAA,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI;AAC/D;AAEA,SAAS,WAAW,CAAC,GAA4B,EAAE,GAAW,EAAA;AAC5D,IAAA,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC;AAClB,IAAA,OAAO,OAAO,CAAC,KAAK,SAAS,GAAG,CAAC,GAAG,IAAI;AAC1C;AAEA;;;;;;AAMG;AACH,SAAS,mBAAmB,CAAC,GAA4B,EAAA;AACvD,IAAA,OAAO,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,QAAQ;AACxC;AAEA,SAAS,mBAAmB,CAAC,GAA4B,EAAA;AACvD,IAAA,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC;AAExB,IAAA,IAAI,IAAI,KAAK,QAAQ,EAAE;QACrB,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC;AAC1C,QAAA,IAAI,OAAO,KAAK,MAAM,EAAE;YACtB,OAAO;AACL,gBAAA,IAAI,EAAE,QAAQ;AACd,gBAAA,OAAO,EAAE,MAAM;gBACf,OAAO,EAAE,CAAA,qBAAA,EAAwB,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,OAAO,CAAA,CAAE;AACtE,gBAAA,GAAG,EAAE,GAAG;aACT;QACH;QACA,IAAI,OAAO,KAAK,cAAc,IAAI,OAAO,KAAK,eAAe,EAAE;YAC7D,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,IAAI,MAAM;YACvD,OAAO;AACL,gBAAA,IAAI,EAAE,QAAQ;AACd,gBAAA,OAAO,EAAE,MAAM;gBACf,OAAO,EACL,OAAO,KAAK;sBACR,CAAA,eAAA,EAAkB,QAAQ,CAAA;sBAC1B,CAAA,iBAAA,EAAoB,QAAQ,CAAA,CAAE;AACpC,gBAAA,GAAG,EAAE,GAAG;aACT;QACH;QACA,OAAO;AACL,YAAA,IAAI,EAAE,QAAQ;AACd,YAAA,OAAO,EAAE,OAAO;AAChB,YAAA,OAAO,EAAE,CAAA,YAAA,EAAe,OAAO,GAAG,KAAK,GAAG,OAAO,GAAG,EAAE,CAAA,CAAE;AACxD,YAAA,GAAG,EAAE,GAAG;SACT;IACH;AAEA,IAAA,IAAI,IAAI,KAAK,kBAAkB,EAAE;AAC/B,QAAA,MAAM,IAAI,GAAG,GAAG,CAAC,iBAAiB,CAAC;QACnC,IAAI,OAAO,GAAG,kBAAkB;AAChC,QAAA,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE;YAClB,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC;YACzC,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,EAAE,eAAe,CAAC;AACvD,YAAA,IAAI,MAAM,IAAI,aAAa,EAAE;AAC3B,gBAAA,OAAO,GAAG,CAAA,WAAA,EAAc,MAAM,CAAA,EAAA,EAAK,aAAa,GAAG;YACrD;QACF;AACA,QAAA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE;IACrE;AAEA,IAAA,IAAI,IAAI,KAAK,WAAW,EAAE;AACxB,QAAA,MAAM,OAAO,GAAG,GAAG,CAAC,SAAS,CAAC;AAC9B,QAAA,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE;YACrB,MAAM,KAAK,GAAG,qBAAqB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACvD,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC;YAC1C,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI;YAChF,OAAO;AACL,gBAAA,IAAI,EAAE,WAAW;gBACjB,KAAK;gBACL,KAAK;gBACL,KAAK;AACL,gBAAA,GAAG,EAAE,GAAG;aACT;QACH;IACF;AAEA,IAAA,IAAI,IAAI,KAAK,MAAM,EAAE;;;AAGnB,QAAA,MAAM,OAAO,GAAG,GAAG,CAAC,SAAS,CAAC;AAC9B,QAAA,IAAI,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE;YAC1D,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,SAAS,CAAC,EAAE;AACrC,gBAAA,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,aAAa,EAAE;AACpD,oBAAA,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC;oBAClC,OAAO;AACL,wBAAA,IAAI,EAAE,aAAa;wBACnB,SAAS,EAAE,UAAU,CAAC,IAAI,EAAE,aAAa,CAAC,IAAI,EAAE;AAChD,wBAAA,OAAO,EAAE,oBAAoB,CAAC,UAAU,CAAC;wBACzC,OAAO,EAAE,WAAW,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,KAAK;AAC/C,wBAAA,GAAG,EAAE,GAAG;qBACT;gBACH;YACF;QACF;IACF;AAEA,IAAA,IAAI,IAAI,KAAK,QAAQ,EAAE;QACrB,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI;QACxE,OAAO;AACL,YAAA,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,EAAE;YACrC,UAAU,EAAE,UAAU,CAAC,GAAG,EAAE,aAAa,CAAC,IAAI,CAAC;YAC/C,KAAK;AACL,YAAA,OAAO,EAAE,UAAU,CAAC,GAAG,EAAE,gBAAgB,CAAC;AAC1C,YAAA,MAAM,EAAE,UAAU,CAAC,GAAG,EAAE,iBAAiB,CAAC,IAAI,UAAU,CAAC,GAAG,EAAE,aAAa,CAAC,IAAI,WAAW;YAC3F,OAAO,EAAE,WAAW,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,KAAK;AAC9C,YAAA,GAAG,EAAE,GAAG;SACT;IACH;IAEA,OAAO;AACL,QAAA,IAAI,EAAE,SAAS;AACf,QAAA,OAAO,EAAE,CAAA,gCAAA,EAAmC,OAAO,IAAI,KAAK,QAAQ,GAAG,IAAI,GAAG,KAAK,CAAA,CAAE;AACrF,QAAA,GAAG,EAAE,GAAG;KACT;AACH;AAEA,SAAS,cAAc,CAAC,GAA4B,EAAA;;;AAGlD,IAAA,OAAO,mBAAmB,CAAC,GAAG,CAAC;AACjC;AAEA,SAAS,qBAAqB,CAAC,OAAgB,EAAA;IAC7C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;AAC3B,QAAA,OAAO,EAAE;IACX;IACA,MAAM,KAAK,GAA0B,EAAE;AACvC,IAAA,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE;AAC1B,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;YACnB;QACF;AACA,QAAA,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,MAAM,EAAE;YAC3B,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;AACrC,YAAA,IAAI,IAAI,KAAK,IAAI,EAAE;gBACjB,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;YACpC;QACF;AAAO,aAAA,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,UAAU,EAAE;YACtC,KAAK,CAAC,IAAI,CAAC;AACT,gBAAA,IAAI,EAAE,UAAU;gBAChB,EAAE,EAAE,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;gBAChC,QAAQ,EAAE,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM;AAC5C,gBAAA,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI;AAC7B,aAAA,CAAC;QACJ;IACF;AACA,IAAA,OAAO,KAAK;AACd;AAEA,SAAS,YAAY,CAAC,KAA8B,EAAA;IAClD,OAAO;QACL,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC;QAC7C,MAAM,EAAE,UAAU,CAAC,KAAK,EAAE,eAAe,CAAC,IAAI,CAAC;QAC/C,SAAS,EAAE,UAAU,CAAC,KAAK,EAAE,yBAAyB,CAAC,IAAI,SAAS;QACpE,aAAa,EAAE,UAAU,CAAC,KAAK,EAAE,6BAA6B,CAAC,IAAI,SAAS;KAC7E;AACH;AAEA;;;;;AAKG;AACH,SAAS,oBAAoB,CAAC,OAAgB,EAAA;AAC5C,IAAA,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE;AAC/B,QAAA,OAAO,OAAO;IAChB;AACA,IAAA,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;AAC1B,QAAA,OAAO;AACJ,aAAA,GAAG,CAAC,CAAC,KAAK,KAAI;AACb,YAAA,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,MAAM,EAAE;gBAC/C,OAAO,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE;YACxC;AACA,YAAA,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;AAC9B,QAAA,CAAC;aACA,IAAI,CAAC,IAAI,CAAC;IACf;IACA,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,IAAI,EAAE;AAC7C,QAAA,OAAO,EAAE;IACX;AACA,IAAA,IAAI;QACF,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC;AAAE,IAAA,MAAM;AACN,QAAA,OAAO,MAAM,CAAC,OAAO,CAAC;IACxB;AACF;;AC3ZA;;;;;;;;;;;;;AAaG;MASU,qBAAqB,CAAA;AACvB,IAAA,MAAM,GAAG,KAAK,CAAC,QAAQ,4EAAuB;AAEvD;;;AAGG;AACgB,IAAA,YAAY,GAAG,QAAQ,CAAgB,MACxD,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,mFAC7B;AAED;;;;AAIG;AACc,IAAA,WAAW,GAAG,MAAM,CAAsB,IAAI,GAAG,EAAE,kFAAC;AAE3D,IAAA,UAAU,CAAC,QAAgB,EAAA;QACnC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC;IACzC;AAEU,IAAA,SAAS,CAAC,QAAgB,EAAA;QAClC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,KAAI;AAC/B,YAAA,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC;AAC1B,YAAA,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;AACtB,gBAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;YACvB;iBAAO;AACL,gBAAA,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;YACpB;AACA,YAAA,OAAO,IAAI;AACb,QAAA,CAAC,CAAC;IACJ;AAEA;;;;AAIG;AACO,IAAA,UAAU,CAAC,GAAW,EAAA;AAC9B,QAAA,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;QACvB,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE;AAC7B,YAAA,OAAO,GAAG;QACZ;AACA,QAAA,OAAO,CAAC,CAAC,kBAAkB,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACzD;;AAGU,IAAA,gBAAgB,CAAC,CAAS,EAAA;AAClC,QAAA,IAAI,CAAC,IAAI,IAAI,EAAE;AACb,YAAA,OAAO,CAAA,EAAG,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA,KAAA,CAAO;QACxC;QACA,OAAO,CAAA,EAAG,CAAC,CAAA,IAAA,CAAM;IACnB;;AAGU,IAAA,cAAc,CAAC,EAAU,EAAA;AACjC,QAAA,IAAI,EAAE,GAAG,IAAI,EAAE;YACb,OAAO,CAAA,EAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK;QAC/B;AACA,QAAA,OAAO,CAAA,EAAG,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA,EAAA,CAAI;IACtC;;AAGU,IAAA,UAAU,CAAC,GAA8B,EAAA;QACjD,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,KAAK,CAAC,EAAE;AAC5B,YAAA,OAAO,GAAG;QACZ;QACA,OAAO,CAAA,CAAA,EAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;IAC7B;;AAGU,IAAA,SAAS,CAAC,KAAc,EAAA;QAChC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE;AACzC,YAAA,OAAO,EAAE;QACX;AACA,QAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AAC7B,YAAA,OAAO,KAAK;QACd;AACA,QAAA,IAAI;YACF,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC;AAAE,QAAA,MAAM;AACN,YAAA,OAAO,MAAM,CAAC,KAAK,CAAC;QACtB;IACF;wGApFW,qBAAqB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;4FAArB,qBAAqB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,mBAAA,EAAA,MAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECjClC,0mLA4IA,EAAA,MAAA,EAAA,CAAA,q4LAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDhHY,YAAY,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAKX,qBAAqB,EAAA,UAAA,EAAA,CAAA;kBARjC,SAAS;+BACE,mBAAmB,EAAA,UAAA,EACjB,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,CAAC,EAAA,eAAA,EAGN,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,0mLAAA,EAAA,MAAA,EAAA,CAAA,q4LAAA,CAAA,EAAA;;;AEtBjD;;;;AAIG;MAQU,mBAAmB,CAAA;AACrB,IAAA,IAAI,GAAG,KAAK,CAAC,QAAQ,0EAAiB;AAE5B,IAAA,QAAQ,GAAG,MAAM,CAAC,KAAK,+EAAC;AAExB,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAK;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM;QACjC,QAAQ,MAAM;AACZ,YAAA,KAAK,WAAW;AACd,gBAAA,OAAO,WAAW;AACpB,YAAA,KAAK,QAAQ;AACX,gBAAA,OAAO,QAAQ;AACjB,YAAA,KAAK,UAAU;AACb,gBAAA,OAAO,UAAU;AACnB,YAAA,KAAK,UAAU;AACf,YAAA,KAAK,SAAS;AACZ,gBAAA,OAAO,SAAS;AAClB,YAAA;AACE,gBAAA,OAAO,SAAS;;AAEtB,IAAA,CAAC,oFAAC;AAEiB,IAAA,aAAa,GAAG,QAAQ,CAAC,MAC1C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,SAAS,CAAC,oFACnC;AACkB,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAK;QAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM;AACjC,QAAA,OAAO,MAAM,KAAK,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;AAC1D,IAAA,CAAC,iFAAC;IAEQ,cAAc,GAAA;AACtB,QAAA,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACjC;AAEQ,IAAA,MAAM,CAAC,KAAc,EAAA;QAC3B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE;AACzC,YAAA,OAAO,EAAE;QACX;AACA,QAAA,IAAI;YACF,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC;AAAE,QAAA,MAAM;AACN,YAAA,OAAO,MAAM,CAAC,KAAK,CAAC;QACtB;IACF;wGA3CW,mBAAmB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAnB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,mBAAmB,2MCrBhC,spCAmCA,EAAA,MAAA,EAAA,CAAA,ouDAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FDda,mBAAmB,EAAA,UAAA,EAAA,CAAA;kBAP/B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,iBAAiB,EAAA,OAAA,EAClB,EAAE,EAAA,eAAA,EAGM,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,spCAAA,EAAA,MAAA,EAAA,CAAA,ouDAAA,CAAA,EAAA;;;AELjD;;;;;;;;;AASG;MAQU,wBAAwB,CAAA;AAC1B,IAAA,OAAO,GAAG,KAAK,CAAC,QAAQ,6EAA0B;IAElD,MAAM,GAAG,MAAM,EAAyB;IACxC,OAAO,GAAG,MAAM,EAAQ;AAEd,IAAA,OAAO,GAAG,MAAM,CAAC,EAAE,8EAAC;AAEpB,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAK;AAC/C,QAAA,IAAI;YACF,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE;AAAE,QAAA,MAAM;AACN,YAAA,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC,OAAO;QAC/B;AACF,IAAA,CAAC,oFAAC;IAEQ,OAAO,GAAA;AACf,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;AACf,YAAA,OAAO,EAAE,UAAU;AACnB,YAAA,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE;AACnC,SAAA,CAAC;IACJ;IAEU,MAAM,GAAA;AACd,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;AACf,YAAA,OAAO,EAAE,UAAU;AACnB,YAAA,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE;AACnC,SAAA,CAAC;IACJ;IAEU,SAAS,GAAA;AACjB,QAAA,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;IACrB;IAEQ,kBAAkB,GAAA;QACxB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE;AACrC,QAAA,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,OAAO,GAAG,SAAS;IACjD;wGArCW,wBAAwB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;4FAAxB,wBAAwB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,sBAAA,EAAA,MAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,MAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EC/BrC,2gDAoDA,EAAA,MAAA,EAAA,CAAA,4mEAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,ED1BY,WAAW,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,oBAAA,EAAA,QAAA,EAAA,8MAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,2CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,OAAA,EAAA,QAAA,EAAA,qDAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,UAAA,EAAA,SAAA,EAAA,gBAAA,CAAA,EAAA,OAAA,EAAA,CAAA,eAAA,CAAA,EAAA,QAAA,EAAA,CAAA,SAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAKV,wBAAwB,EAAA,UAAA,EAAA,CAAA;kBAPpC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,sBAAsB,WACvB,CAAC,WAAW,CAAC,EAAA,eAAA,EAGL,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,2gDAAA,EAAA,MAAA,EAAA,CAAA,4mEAAA,CAAA,EAAA;;;AE3BjD;;;;;AAKG;MAQU,yBAAyB,CAAA;AAC3B,IAAA,aAAa,GAAG,KAAK,CAAC,QAAQ,mFAAU;AACxC,IAAA,QAAQ,GAAG,KAAK,CAAC,QAAQ,8EAAU;AACnC,IAAA,eAAe,GAAG,KAAK,CAAgB,IAAI,sFAAC;AAC5C,IAAA,UAAU,GAAG,KAAK,CAAgB,IAAI,iFAAC;IAE7B,YAAY,GAAG,QAAQ,CAAC,MACzC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,cAAA,EAAA,CAAA,8BAAA,EAAA,CAAA,CACpD;AACkB,IAAA,cAAc,GAAG,QAAQ,CAAC,MAAK;AAChD,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,EAAE;AACvC,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE;QAC7B,IAAI,QAAQ,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE;AACnC,YAAA,OAAO,IAAI;QACb;QACA,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;AACpC,IAAA,CAAC,qFAAC;AAEiB,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,iFAAC;AAC9D,IAAA,YAAY,GAAG,QAAQ,CAAC,MAAK;AAC9C,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,EAAE;AACjC,QAAA,OAAO,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;AAC/C,IAAA,CAAC,mFAAC;IAEM,OAAO,CAAC,QAAgB,EAAE,GAAW,EAAA;AAC3C,QAAA,IAAI,GAAG,IAAI,CAAC,EAAE;AACZ,YAAA,OAAO,CAAC;QACV;AACA,QAAA,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC;IAC1D;AAEQ,IAAA,OAAO,CAAC,OAAe,EAAA;AAC7B,QAAA,IAAI,OAAO,IAAI,GAAG,EAAE;AAClB,YAAA,OAAO,UAAU;QACnB;AACA,QAAA,IAAI,OAAO,IAAI,EAAE,EAAE;AACjB,YAAA,OAAO,SAAS;QAClB;AACA,QAAA,OAAO,IAAI;IACb;wGAvCW,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAzB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,yBAAyB,yoBCftC,q4BAuBA,EAAA,MAAA,EAAA,CAAA,s0BAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FDRa,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBAPrC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,uBAAuB,EAAA,OAAA,EACxB,EAAE,EAAA,eAAA,EAGM,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,q4BAAA,EAAA,MAAA,EAAA,CAAA,s0BAAA,CAAA,EAAA;;;AECjD;;;;;;;;;;AAUG;MAQU,gCAAgC,CAAA;AAC1B,IAAA,MAAM,GAAG,MAAM,CAAC,sBAAsB,CAAC;AAErC,IAAA,KAAK,GAAG,MAAM,CAC/B,oBAAoB,4EACrB;AACkB,IAAA,UAAU,GAAG,MAAM,CAAS,CAAC,iFAAC;AAE9B,IAAA,OAAO,GAAG,MAAM,CAAU,KAAK,8EAAC;AAChC,IAAA,MAAM,GAAG,MAAM,CAChC,IAAI,6EACL;AACkB,IAAA,KAAK,GAAG,MAAM,CAAgB,IAAI,4EAAC;AACnC,IAAA,MAAM,GAAG,MAAM,CAAU,KAAK,6EAAC;;AAG/B,IAAA,MAAM,GAInB;AACJ,QAAA;AACE,YAAA,KAAK,EAAE,oBAAoB;AAC3B,YAAA,KAAK,EAAE,oBAAoB;AAC3B,YAAA,IAAI,EAAE,yEAAyE;AAChF,SAAA;AACD,QAAA;AACE,YAAA,KAAK,EAAE,mBAAmB;AAC1B,YAAA,KAAK,EAAE,mBAAmB;AAC1B,YAAA,IAAI,EAAE,sEAAsE;AAC7E,SAAA;KACF;IAES,OAAO,GAAA;AACf,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE;YAClB;QACF;AACA,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,EAAE;AAChC,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;AACtB,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AACpB,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;AACtB,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;AAErB,QAAA,IAAI,CAAC;AACF,aAAA,qBAAqB,CAAC;AACrB,YAAA,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE;AACnB,YAAA,UAAU,EAAE,GAAG;SAChB;AACA,aAAA,SAAS,CAAC;AACT,YAAA,IAAI,EAAE,CAAC,QAAQ,KAAI;AACjB,gBAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;AACzB,gBAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;YACzB,CAAC;AACD,YAAA,KAAK,EAAE,CAAC,GAAY,KAAI;AACtB,gBAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;AACrC,gBAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;YACzB,CAAC;AACF,SAAA,CAAC;IACN;IAEU,MAAM,GAAA;QACd,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI;QAChC,IAAI,CAAC,IAAI,EAAE;YACT;QACF;;;;QAIA,IAAI,SAAS,CAAC,SAAS,IAAI,SAAS,CAAC,SAAS,CAAC,SAAS,EAAE;AACxD,YAAA,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CACtC,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAC3B,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAC7B;QACH;IACF;IAEU,OAAO,GAAA;AACf,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;AACrB,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;IACxB;IAEQ,aAAa,GAAA;QACnB,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;;;QAGvC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;AAC5B,YAAA,OAAO,CAAC;QACV;AACA,QAAA,IAAI,CAAC,GAAG,EAAE,EAAE;AACV,YAAA,OAAO,EAAE;QACX;AACA,QAAA,OAAO,CAAC;IACV;AAEQ,IAAA,WAAW,CAAC,GAAY,EAAA;QAC9B,IACE,OAAO,GAAG,KAAK,QAAQ;AACvB,YAAA,GAAG,KAAK,IAAI;AACZ,YAAA,QAAQ,IAAI,GAAG;AACf,YAAA,OAAQ,GAA0B,CAAC,MAAM,KAAK,QAAQ,EACtD;AACA,YAAA,MAAM,MAAM,GAAI,GAA0B,CAAC,MAAM;YACjD,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,EAAE;AACpC,gBAAA,OAAO,wDAAwD;YACjE;AACA,YAAA,IAAI,MAAM,KAAK,GAAG,EAAE;AAClB,gBAAA,OAAO,0CAA0C;YACnD;YACA,OAAO,CAAA,qBAAA,EAAwB,MAAM,CAAA,CAAA,CAAG;QAC1C;AACA,QAAA,OAAO,qDAAqD;IAC9D;wGA/GW,gCAAgC,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAhC,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,gCAAgC,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,+BAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EChC7C,wxGAqHA,EAAA,MAAA,EAAA,CAAA,q0FAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,ED1FY,YAAY,8BAAE,WAAW,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,aAAA,EAAA,QAAA,EAAA,8CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,cAAA,EAAA,QAAA,EAAA,QAAA,EAAA,MAAA,EAAA,CAAA,SAAA,EAAA,OAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,uBAAA,EAAA,QAAA,EAAA,QAAA,EAAA,MAAA,EAAA,CAAA,SAAA,EAAA,OAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,oBAAA,EAAA,QAAA,EAAA,8MAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,mBAAA,EAAA,QAAA,EAAA,iGAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,0BAAA,EAAA,QAAA,EAAA,6GAAA,EAAA,MAAA,EAAA,CAAA,aAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,2CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,oBAAA,EAAA,QAAA,EAAA,sGAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,YAAA,EAAA,QAAA,EAAA,gHAAA,EAAA,MAAA,EAAA,CAAA,KAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,YAAA,EAAA,QAAA,EAAA,gHAAA,EAAA,MAAA,EAAA,CAAA,KAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,OAAA,EAAA,QAAA,EAAA,qDAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,UAAA,EAAA,SAAA,EAAA,gBAAA,CAAA,EAAA,OAAA,EAAA,CAAA,eAAA,CAAA,EAAA,QAAA,EAAA,CAAA,SAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,MAAA,EAAA,QAAA,EAAA,yEAAA,EAAA,MAAA,EAAA,CAAA,eAAA,CAAA,EAAA,OAAA,EAAA,CAAA,UAAA,CAAA,EAAA,QAAA,EAAA,CAAA,QAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAKxB,gCAAgC,EAAA,UAAA,EAAA,CAAA;kBAP5C,SAAS;+BACE,+BAA+B,EAAA,OAAA,EAChC,CAAC,YAAY,EAAE,WAAW,CAAC,EAAA,eAAA,EAGnB,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,wxGAAA,EAAA,MAAA,EAAA,CAAA,q0FAAA,CAAA,EAAA;;;AEnBjD;;;;;;;;;;;;;;;AAeG;MAQU,uBAAuB,CAAA;;AAEzB,IAAA,SAAS,GAAG,KAAK,CAAC,QAAQ,+EAA+B;;AAGzD,IAAA,UAAU,GAAG,KAAK,CAAS,EAAE,iFAAC;AAEvC;;;;AAIG;IACM,SAAS,GAAG,MAAM,EAAsB;AAEjD;;;AAGG;IACM,SAAS,GAAG,MAAM,EAAQ;;AAGhB,IAAA,kBAAkB,GAAG,MAAM,CAAC,EAAE,yFAAC;AAC/B,IAAA,WAAW,GAAG,MAAM,CAAC,EAAE,kFAAC;AACxB,IAAA,aAAa,GAAG,MAAM,CAAC,EAAE,oFAAC;AAE1B,IAAA,gBAAgB,GAAG,QAAQ,CAA0B,MAAK;AAC3E,QAAA,MAAM,EAAE,GAAG,IAAI,CAAC,kBAAkB,EAAE;QACpC,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,IAAI;AAC1D,IAAA,CAAC,uFAAC;AAEF;;;;AAIG;AACgB,IAAA,YAAY,GAAG,QAAQ,CAAC,MAAK;AAC9C,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,EAAE;QACxC,IAAI,CAAC,QAAQ,EAAE;AACb,YAAA,OAAO,EAAE;QACX;AACA,QAAA,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE;AAC7B,YAAA,OAAO,EAAE;QACX;QACA,OAAO,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,YAAY,CAAC;AAChD,IAAA,CAAC,mFAAC;;AAGiB,IAAA,iBAAiB,GAAG,QAAQ,CAAC,MAAK;AACnD,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,EAAE;QACxC,IAAI,CAAC,QAAQ,EAAE;AACb,YAAA,OAAO,KAAK;QACd;QACA,OAAO,QAAQ,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,GAAG,KAAI;YACjD,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;AAChC,YAAA,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;AAClD,QAAA,CAAC,CAAC;AACJ,IAAA,CAAC,wFAAC;IAEQ,QAAQ,GAAA;AAChB,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,EAAE;QACxC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE;YAC1C;QACF;AACA,QAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YAClB,UAAU,EAAE,QAAQ,CAAC,EAAE;YACvB,OAAO,EAAE,QAAQ,CAAC,OAAO;AACzB,YAAA,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE;AACtC,YAAA,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE;YAC1C,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,YAAY,CAAC;AAC9C,SAAA,CAAC;IACJ;IAEU,QAAQ,GAAA;AAChB,QAAA,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE;IACvB;;AAGQ,IAAA,QAAQ,CAAC,WAAmB,EAAA;QAClC,QAAQ,WAAW;AACjB,YAAA,KAAK,aAAa;AAChB,gBAAA,OAAO,IAAI,CAAC,WAAW,EAAE;AAC3B,YAAA,KAAK,eAAe;AAClB,gBAAA,OAAO,IAAI,CAAC,aAAa,EAAE;AAC7B,YAAA;AACE,gBAAA,OAAO,IAAI;;IAEjB;AAEQ,IAAA,WAAW,CAAC,QAAgB,EAAA;QAClC,OAAO,QAAQ,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC,IAAI,EAAE,GAAG,KAAI;YACxD,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;;;AAGhC,YAAA,OAAO,KAAK,KAAK,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,GAAG,IAAI;AAC7C,QAAA,CAAC,CAAC;IACJ;wGA/FW,uBAAuB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;4FAAvB,uBAAuB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,sBAAA,EAAA,MAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,SAAA,EAAA,WAAA,EAAA,SAAA,EAAA,WAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EClCpC,0xGAwGA,EAAA,MAAA,EAAA,CAAA,ouFAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,ED3EY,WAAW,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,oBAAA,EAAA,QAAA,EAAA,8MAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,2CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,OAAA,EAAA,QAAA,EAAA,qDAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,UAAA,EAAA,SAAA,EAAA,gBAAA,CAAA,EAAA,OAAA,EAAA,CAAA,eAAA,CAAA,EAAA,QAAA,EAAA,CAAA,SAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAKV,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBAPnC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,sBAAsB,WACvB,CAAC,WAAW,CAAC,EAAA,eAAA,EAGL,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,0xGAAA,EAAA,MAAA,EAAA,CAAA,ouFAAA,CAAA,EAAA;;;AEhCjD;;;;;;AAMG;;ACNH;;AAEG;;;;"}
|