@meshmakers/octo-ai-console 3.3.1210 → 3.4.70

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.
@@ -203,6 +203,92 @@ interface IssueAiCredentialTicketResponseDto {
203
203
  readonly scope: AiCredentialTicketScope;
204
204
  }
205
205
 
206
+ /**
207
+ * Body for `POST /{tenantId}/v1/credentials/github-pat`. The plaintext token is
208
+ * validated against GitHub's `/user` endpoint before encryption — a refused
209
+ * token comes back as HTTP 422 + `error: 'github_refused_token'`.
210
+ */
211
+ interface RegisterAiGitHubPatRequestDto {
212
+ /** Operator-supplied label so PATs are tellable apart in the list view. */
213
+ readonly name: string;
214
+ /** Plaintext token. Never persisted client-side; cleared from the form on submit. */
215
+ readonly token: string;
216
+ /** Free-form scope hint — purely informational. */
217
+ readonly scope?: string;
218
+ }
219
+ /**
220
+ * Wire shape returned by `GET /{tenantId}/v1/credentials/github-pat`. Carries
221
+ * neither the plaintext token nor the encrypted ciphertext — only enough to
222
+ * render a row + offer Revoke.
223
+ */
224
+ interface AiGitHubPatDto {
225
+ /** Binding rtId — pass to delete. */
226
+ readonly rtId: string;
227
+ /** Operator-supplied label from registration. */
228
+ readonly name: string;
229
+ /** Operator-supplied free-form scope hint. */
230
+ readonly scope: string;
231
+ /** Last four characters of the plaintext token preceded by "…" (e.g. "…ABCD"). */
232
+ readonly maskedTail: string;
233
+ /**
234
+ * GitHub login the token authenticated as during registration. Null when the
235
+ * binding's ciphertext is unreadable (rotation gone wrong) — UI should hint
236
+ * "revoke and re-register" in that case.
237
+ */
238
+ readonly gitHubLogin?: string | null;
239
+ }
240
+
241
+ /**
242
+ * Wire shape returned by `GET /{tenantId}/v1/app-templates` (#4145). Each entry
243
+ * is one option in the Studio's "New App from Template" wizard. The wizard
244
+ * fills the goal placeholders and uses `jobKind` when it POSTs `/sessions`.
245
+ *
246
+ * Mirrors `AppTemplateResponse` in the adapter
247
+ * (`src/AiServices/TenantApi/v1/Models/AppTemplateResponse.cs`). The JSON is
248
+ * camel-cased on the wire; the C# DTO is PascalCased server-side.
249
+ */
250
+ interface AiAppTemplateDto {
251
+ /** Stable identifier the wizard echoes on submit (`crud-list`, `dashboard`, `form-only`). */
252
+ readonly id: string;
253
+ /** Display name for the template picker. */
254
+ readonly name: string;
255
+ /** One-paragraph description shown next to the picker entry. */
256
+ readonly description: string;
257
+ /**
258
+ * JobKind enum key the adapter expects when the wizard POSTs the session.
259
+ * The wire is the CK enum key as string — see `AiJobKind` for the values
260
+ * the adapter currently emits. Today every Phase-1 template uses
261
+ * `Application`.
262
+ */
263
+ readonly jobKind: AiJobKind;
264
+ /**
265
+ * Handlebars-style template the wizard interpolates with operator inputs
266
+ * (e.g. `{{projectName}}`, `{{primaryEntity}}`). The rendered text is what
267
+ * goes onto `CreateSessionRequestDto.goal` — the adapter never interpolates,
268
+ * so the operator sees the final Goal in the wizard before submit.
269
+ */
270
+ readonly goalTemplate: string;
271
+ /**
272
+ * Names of the placeholders the wizard must collect before enabling Submit.
273
+ * Drives the form's required-field gate without the wizard having to parse
274
+ * `goalTemplate`.
275
+ */
276
+ readonly requiredPlaceholders: readonly string[];
277
+ }
278
+ /**
279
+ * The shape the wizard emits on Submit. The host page is expected to render
280
+ * `goal` (interpolated from the picked template) into a
281
+ * `CreateSessionRequestDto` and call `createSession` on the adapter client.
282
+ */
283
+ interface AiNewAppSubmission {
284
+ readonly templateId: string;
285
+ readonly jobKind: AiJobKind;
286
+ readonly projectName: string;
287
+ readonly primaryEntity: string;
288
+ /** The fully interpolated Goal string — what the wizard would POST on submit. */
289
+ readonly goal: string;
290
+ }
291
+
206
292
  /**
207
293
  * Configuration the host application supplies via {@link provideOctoAiConsole}.
208
294
  * Every field is required so the library never falls back to a hard-coded URL or
@@ -320,6 +406,31 @@ declare class AiAdapterClientService {
320
406
  * once the worker has acknowledged.
321
407
  */
322
408
  cancelSession(sessionId: string): Observable<AiSessionDto>;
409
+ /**
410
+ * `DELETE /{tenantId}/v1/sessions/{sessionId}` — hard-delete a terminal
411
+ * session. The server refuses non-terminal sessions with HTTP 409 and the
412
+ * <code>invalid_state_transition</code> error code, so callers should cancel
413
+ * first when the session is still in flight. 204 on success; the response
414
+ * has no body.
415
+ */
416
+ deleteSession(sessionId: string): Observable<void>;
417
+ /**
418
+ * `POST /{tenantId}/v1/sessions/{sessionId}/messages` — append a follow-up
419
+ * user turn to a `Completed` session. The adapter re-materialises the
420
+ * workspace (fresh credential snapshot), recovers Claude's session id from
421
+ * the persisted `system/init` event of the original turn, and spawns
422
+ * <code>claude --resume &lt;id&gt;</code> so the conversation context
423
+ * carries over. The next turn's events stream on the same SignalR channel
424
+ * as the prior turn — there is no separate subscription to set up.
425
+ *
426
+ * Server response is 202 with the updated session DTO (status flipped back
427
+ * to `Running`); the caller can use the returned status to drive an
428
+ * optimistic UI update. Non-Completed sessions return 409 with
429
+ * <code>invalid_state_transition</code>; a session whose first turn died
430
+ * before claude emitted init returns 409 with
431
+ * <code>claude_session_id_not_captured</code>.
432
+ */
433
+ sendMessage(sessionId: string, text: string): Observable<AiSessionDto>;
323
434
  /**
324
435
  * `GET /{tenantId}/v1/sessions/{sessionId}/events?sinceSequence=N` — replay
325
436
  * persisted events for a session. UI uses this on reconnect to backfill any
@@ -342,6 +453,34 @@ declare class AiAdapterClientService {
342
453
  * on a different machine — they use the bastion CLI, not the Studio.
343
454
  */
344
455
  issueCredentialTicket(request: IssueAiCredentialTicketRequestDto): Observable<IssueAiCredentialTicketResponseDto>;
456
+ /**
457
+ * `POST /{tenantId}/v1/credentials/github-pat` — register a GitHub Personal
458
+ * Access Token (#4124). The adapter validates the token against GitHub's
459
+ * `/user` endpoint before encryption; a refused token surfaces as HTTP 422
460
+ * with `error: 'github_refused_token'`. The response is intentionally minimal
461
+ * (`{ rtId }`); the host re-fetches the list so the rendering path stays
462
+ * shared with refresh.
463
+ */
464
+ registerGitHubPat(request: RegisterAiGitHubPatRequestDto): Observable<{
465
+ rtId: string;
466
+ }>;
467
+ /**
468
+ * `GET /{tenantId}/v1/credentials/github-pat` — list registered PATs with a
469
+ * masked tail + GitHub login. Plaintext never crosses the wire.
470
+ */
471
+ listGitHubPats(): Observable<AiGitHubPatDto[]>;
472
+ /**
473
+ * `DELETE /{tenantId}/v1/credentials/github-pat/{rtId}` — revoke a PAT. The
474
+ * ciphertext is deleted, not soft-tombstoned. Returns void on 204.
475
+ */
476
+ deleteGitHubPat(rtId: string): Observable<void>;
477
+ /**
478
+ * `GET /{tenantId}/v1/app-templates` — list the Custom-App templates the
479
+ * adapter exposes for the wizard (#4145). Phase-1 returns the same three
480
+ * static entries for every tenant; the wire shape is stable so per-tenant
481
+ * overrides can ride it without an SPA bump.
482
+ */
483
+ getAppTemplates(): Observable<AiAppTemplateDto[]>;
345
484
  private tenantBase;
346
485
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AiAdapterClientService, never>;
347
486
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<AiAdapterClientService>;
@@ -426,38 +565,159 @@ declare class AiSessionStreamService {
426
565
  * the component takes the array as an input signal and emits the rtId on
427
566
  * row click. Keeping it data-source-agnostic lets refinery wire its own
428
567
  * Apollo cache while a bastion CLI could pass static fixtures in a Storybook.
568
+ *
569
+ * Cancel and Delete are surfaced as inline per-row actions and emitted as
570
+ * separate outputs — the host owns the confirmation dialog and the actual
571
+ * REST call. The component only knows which actions are valid for which
572
+ * status, so a Running session never offers Delete and a Completed session
573
+ * never offers Cancel.
429
574
  */
430
575
  declare class AiSessionListComponent {
431
576
  readonly sessions: _angular_core.InputSignal<AiSessionDto[]>;
432
577
  readonly selectedSessionId: _angular_core.InputSignal<string | null>;
433
578
  readonly sessionSelected: _angular_core.OutputEmitterRef<string>;
579
+ readonly cancelRequested: _angular_core.OutputEmitterRef<string>;
580
+ readonly deleteRequested: _angular_core.OutputEmitterRef<string>;
434
581
  protected readonly orderedSessions: _angular_core.Signal<AiSessionDto[]>;
582
+ /**
583
+ * Terminal status set, kept in sync with the C# `AiAgentSessionService.IsTerminal`
584
+ * predicate. Delete is gated to this set; Cancel is gated to its complement.
585
+ * If the wire enum grows a new terminal kind, both predicates must be
586
+ * updated here AND on the server.
587
+ */
588
+ private static readonly TERMINAL_STATUSES;
589
+ protected canCancel(status: AiSessionStatus): boolean;
590
+ protected canDelete(status: AiSessionStatus): boolean;
435
591
  protected onSelect(sessionRtId: string): void;
592
+ protected onCancel(sessionRtId: string, event: MouseEvent): void;
593
+ protected onDelete(sessionRtId: string, event: MouseEvent): void;
436
594
  protected age(startedAt: string): string;
437
595
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AiSessionListComponent, never>;
438
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<AiSessionListComponent, "mm-ai-session-list", never, { "sessions": { "alias": "sessions"; "required": true; "isSignal": true; }; "selectedSessionId": { "alias": "selectedSessionId"; "required": false; "isSignal": true; }; }, { "sessionSelected": "sessionSelected"; }, never, never, true, never>;
596
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AiSessionListComponent, "mm-ai-session-list", never, { "sessions": { "alias": "sessions"; "required": true; "isSignal": true; }; "selectedSessionId": { "alias": "selectedSessionId"; "required": false; "isSignal": true; }; }, { "sessionSelected": "sessionSelected"; "cancelRequested": "cancelRequested"; "deleteRequested": "deleteRequested"; }, never, never, true, never>;
597
+ }
598
+
599
+ /**
600
+ * Token-usage summary extracted from an `assistant` or `result` payload.
601
+ * Fields beyond `input` / `output` are optional because the upstream worker
602
+ * (claude-code stream-json) sometimes omits them on rate-limited or
603
+ * cache-only turns.
604
+ */
605
+ interface ParsedTokenUsage {
606
+ readonly input: number;
607
+ readonly output: number;
608
+ readonly cacheRead?: number;
609
+ readonly cacheCreation?: number;
610
+ }
611
+ /**
612
+ * A single content fragment inside an assistant event. The worker emits a
613
+ * mix of `text` and `tool_use` parts per message — sometimes one of each in
614
+ * the same payload — and the renderer needs to walk them in order to keep
615
+ * the conversational flow intact (text → "I'll run X" → tool_use card).
616
+ */
617
+ type ParsedAssistantPart = {
618
+ readonly type: 'text';
619
+ readonly text: string;
620
+ } | {
621
+ readonly type: 'tool_use';
622
+ readonly id: string;
623
+ readonly toolName: string;
624
+ readonly input: unknown;
625
+ };
626
+ /**
627
+ * Discriminated union the chat-stream renders on. The component switches over
628
+ * `kind` and pulls only the fields it needs per branch — no `any` walks of
629
+ * the original payload at render time.
630
+ *
631
+ * `raw` is kept on every variant so a debug-view ("show me the original
632
+ * stream-json") can surface it on demand without re-parsing.
633
+ */
634
+ type ParsedView = {
635
+ readonly kind: 'system';
636
+ readonly subKind: 'init' | 'hook' | 'rate-limit' | 'other';
637
+ readonly summary: string;
638
+ readonly raw: unknown;
639
+ } | {
640
+ readonly kind: 'assistant';
641
+ readonly parts: readonly ParsedAssistantPart[];
642
+ readonly model: string | null;
643
+ readonly usage: ParsedTokenUsage | null;
644
+ readonly raw: unknown;
645
+ } | {
646
+ readonly kind: 'tool-result';
647
+ readonly toolUseId: string;
648
+ readonly content: string;
649
+ readonly isError: boolean;
650
+ readonly raw: unknown;
651
+ } | {
652
+ readonly kind: 'result';
653
+ readonly text: string;
654
+ readonly durationMs: number;
655
+ readonly usage: ParsedTokenUsage | null;
656
+ readonly costUsd: number | null;
657
+ readonly reason: string;
658
+ readonly isError: boolean;
659
+ readonly raw: unknown;
660
+ } | {
661
+ readonly kind: 'status-change';
662
+ readonly status: string;
663
+ readonly raw: unknown;
664
+ } | {
665
+ readonly kind: 'unknown';
666
+ readonly summary: string;
667
+ readonly raw: unknown;
668
+ };
669
+ /**
670
+ * Result of one parse step — wraps the source event so the renderer can still
671
+ * carry `sequence` / `at` / `actorRef` through to the template.
672
+ */
673
+ interface ParsedEvent {
674
+ readonly event: AiSessionEventDto;
675
+ readonly view: ParsedView;
439
676
  }
440
677
 
441
678
  /**
442
- * Read-only transcript of a session, derived from the events stream. The
443
- * component takes the events as an input signal so the host owns lifetime —
444
- * a parent that's already wired `AiSessionStreamService.events$` into a
445
- * `toSignal()` just hands the same signal in.
679
+ * Read-only transcript of a session, derived from the events stream.
446
680
  *
447
- * Markdown rendering is deliberately minimal in Phase 1: the component shows
448
- * the raw payload (already JSON or stream-json) in a `<pre>` block. The
449
- * concept §10 calls for proper markdown + code-block highlighting; that
450
- * upgrade lands behind a `markdown` Input once we've picked a renderer
451
- * (markdown-it vs marked) and verified its CSP behaviour.
681
+ * The component takes raw `AiSessionEventDto` entries, runs them through the
682
+ * `event-parser` to produce a typed `ParsedView` per event, and renders each
683
+ * kind with its own treatment assistant bubbles for chat text, expandable
684
+ * cards for tool calls + tool results, single-line collapsed strips for the
685
+ * system / hook noise, and a stats panel for the terminal `result` summary.
686
+ *
687
+ * Markdown rendering inside assistant text is still deferred (concept §10);
688
+ * for now the parser hands plain text to the template and the SCSS preserves
689
+ * line breaks via `white-space: pre-wrap`. Picking a renderer (markdown-it vs
690
+ * marked) is gated on a CSP review and a markdown-corpus regression test.
452
691
  */
453
692
  declare class AiChatStreamComponent {
454
693
  readonly events: _angular_core.InputSignal<AiSessionEventDto[]>;
455
694
  /**
456
- * Sorted by sequence so out-of-order arrival (the SignalR + REST backfill
457
- * union) renders deterministically.
695
+ * Sorted + parsed. Recomputes whenever the input signal changes; the sort
696
+ * is stable so live appends from SignalR keep prior render positions.
697
+ */
698
+ protected readonly parsedEvents: _angular_core.Signal<ParsedEvent[]>;
699
+ /**
700
+ * Per-event "show raw JSON" toggle. Keyed by sequence so live appends
701
+ * don't flicker existing toggles closed. A `Set<number>` rather than a
702
+ * record so add / delete avoids `keyof` shenanigans.
703
+ */
704
+ private readonly expandedSet;
705
+ protected isExpanded(sequence: number): boolean;
706
+ protected toggleRaw(sequence: number): void;
707
+ /**
708
+ * `HH:MM:SS` formatter — full ISO is too noisy in a 30-event transcript.
709
+ * Returns the input unchanged if parsing fails so we don't pretend events
710
+ * we can't read are recent.
458
711
  */
459
- protected readonly orderedEvents: _angular_core.Signal<AiSessionEventDto[]>;
460
- protected variant(kind: AiSessionEventDto['kind']): string;
712
+ protected formatTime(iso: string): string;
713
+ /** Compact "1234 tok" for the assistant header. */
714
+ protected formatTokenCount(n: number): string;
715
+ /** Duration in either `ms` (sub-second) or `s.SS`. */
716
+ protected formatDuration(ms: number): string;
717
+ /** Cost in EUR-style decimals; null + zero collapse to a dash. */
718
+ protected formatCost(usd: number | null | undefined): string;
719
+ /** Pretty-print arbitrary tool_use input or raw blocks for the expandable detail panel. */
720
+ protected formatRaw(value: unknown): string;
461
721
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AiChatStreamComponent, never>;
462
722
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<AiChatStreamComponent, "mm-ai-chat-stream", never, { "events": { "alias": "events"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
463
723
  }
@@ -571,5 +831,59 @@ declare class AiCredentialTicketIssueComponent {
571
831
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<AiCredentialTicketIssueComponent, "mm-ai-credential-ticket-issue", never, {}, {}, never, never, true, never>;
572
832
  }
573
833
 
574
- export { AI_ADAPTER_OPTIONS, AI_SESSION_STREAM_CONNECTION_FACTORY, AiAdapterClientService, AiApprovalModalComponent, AiChatStreamComponent, AiCredentialTicketIssueComponent, AiJobStatusBadgeComponent, AiQuotaIndicatorComponent, AiSessionListComponent, AiSessionStreamService, AiToolCallComponent, provideOctoAiConsole };
575
- export type { AiAdapterOptions, AiApprovalDecidedDto, AiApprovalDecisionDto, AiApprovalOutcome, AiApprovalReason, AiApprovalRequestedDto, AiCredentialTicketScope, AiHubConnectionLike, AiJobKind, AiQuotaSeverity, AiQuotaSnapshotDto, AiQuotaWarningDto, AiSessionDto, AiSessionEventDto, AiSessionEventKind, AiSessionStatus, AiSessionStatusChangedDto, AiSessionStream, AiSessionStreamConnectionFactory, AiToolCallDto, CreateSessionRequestDto, CreateSessionResponseDto, IssueAiCredentialTicketRequestDto, IssueAiCredentialTicketResponseDto };
834
+ /**
835
+ * "New App from Template" wizard (#4145). Presentation-only the host fetches
836
+ * the template catalogue via {@link AiAdapterClientService.getAppTemplates},
837
+ * feeds it into the `templates` input, and listens for `submit` / `cancel` to
838
+ * call `createSession`.
839
+ *
840
+ * The component is intentionally framework-agnostic (no Kendo, no CDK overlay)
841
+ * so consumers can wrap it in whatever modal shell they already have — same
842
+ * convention as `AiApprovalModalComponent`.
843
+ *
844
+ * The interpolation engine is *deliberately* dumb: a regex substitution over
845
+ * `{{placeholder}}` tokens. The wizard only collects two inputs (project name +
846
+ * primary entity), and a full Handlebars dependency for two tokens would be
847
+ * overkill. If a future template needs richer rendering, switch this to
848
+ * `handlebars.precompile`-at-build-time and re-evaluate.
849
+ */
850
+ declare class AiNewAppDialogComponent {
851
+ /** Available templates from the adapter — typically 3 in Phase 1. */
852
+ readonly templates: _angular_core.InputSignal<readonly AiAppTemplateDto[]>;
853
+ /** Tenant name shown in the dialog header so the operator confirms the scope. */
854
+ readonly tenantName: _angular_core.InputSignal<string>;
855
+ /**
856
+ * Emitted on Submit with the fully interpolated Goal + the picked template id.
857
+ * Named `confirmed` (not `submit`) because Angular flags native-event collisions
858
+ * — `submit` is a DOM event and triggers `@angular-eslint/no-output-native`.
859
+ */
860
+ readonly confirmed: _angular_core.OutputEmitterRef<AiNewAppSubmission>;
861
+ /**
862
+ * Emitted on Cancel / dismiss. The host hides the wizard on this signal.
863
+ * Named `cancelled` for the same reason `confirmed` is not `submit`.
864
+ */
865
+ readonly cancelled: _angular_core.OutputEmitterRef<void>;
866
+ /** rtId-style id of the picked template, or empty until the operator picks one. */
867
+ protected readonly selectedTemplateId: _angular_core.WritableSignal<string>;
868
+ protected readonly projectName: _angular_core.WritableSignal<string>;
869
+ protected readonly primaryEntity: _angular_core.WritableSignal<string>;
870
+ protected readonly selectedTemplate: _angular_core.Signal<AiAppTemplateDto | null>;
871
+ /**
872
+ * Rendered Goal string the operator sees in the preview block. Stays empty
873
+ * until a template is picked AND every required placeholder has a value, so
874
+ * the operator never sees a half-interpolated string.
875
+ */
876
+ protected readonly renderedGoal: _angular_core.Signal<string>;
877
+ /** True when every placeholder the picked template declares has a non-empty value. */
878
+ protected readonly allRequiredFilled: _angular_core.Signal<boolean>;
879
+ protected onSubmit(): void;
880
+ protected onCancel(): void;
881
+ /** Read the signal that backs the named placeholder, or `null` if unknown. */
882
+ private valueFor;
883
+ private interpolate;
884
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<AiNewAppDialogComponent, never>;
885
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AiNewAppDialogComponent, "mm-ai-new-app-dialog", never, { "templates": { "alias": "templates"; "required": true; "isSignal": true; }; "tenantName": { "alias": "tenantName"; "required": false; "isSignal": true; }; }, { "confirmed": "confirmed"; "cancelled": "cancelled"; }, never, never, true, never>;
886
+ }
887
+
888
+ export { AI_ADAPTER_OPTIONS, AI_SESSION_STREAM_CONNECTION_FACTORY, AiAdapterClientService, AiApprovalModalComponent, AiChatStreamComponent, AiCredentialTicketIssueComponent, AiJobStatusBadgeComponent, AiNewAppDialogComponent, AiQuotaIndicatorComponent, AiSessionListComponent, AiSessionStreamService, AiToolCallComponent, provideOctoAiConsole };
889
+ export type { AiAdapterOptions, AiAppTemplateDto, AiApprovalDecidedDto, AiApprovalDecisionDto, AiApprovalOutcome, AiApprovalReason, AiApprovalRequestedDto, AiCredentialTicketScope, AiGitHubPatDto, AiHubConnectionLike, AiJobKind, AiNewAppSubmission, AiQuotaSeverity, AiQuotaSnapshotDto, AiQuotaWarningDto, AiSessionDto, AiSessionEventDto, AiSessionEventKind, AiSessionStatus, AiSessionStatusChangedDto, AiSessionStream, AiSessionStreamConnectionFactory, AiToolCallDto, CreateSessionRequestDto, CreateSessionResponseDto, IssueAiCredentialTicketRequestDto, IssueAiCredentialTicketResponseDto, RegisterAiGitHubPatRequestDto };