@newheap/platform-ai-chat 0.16.0 → 0.18.0

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.
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
2
2
  import { InjectionToken, inject, DestroyRef, Injectable, EnvironmentInjector, runInInjectionContext, PLATFORM_ID, signal, computed, effect, untracked, Injector, afterNextRender, makeEnvironmentProviders, provideEnvironmentInitializer, input, ChangeDetectionStrategy, Component, ChangeDetectorRef, Pipe, output, ElementRef, viewChild, HostListener, linkedSignal, ViewContainerRef } from '@angular/core';
3
3
  import { TranslateService, TranslatePipe } from '@ngx-translate/core';
4
4
  import { merge, Observable, of, forkJoin, map, Subject, catchError, throwError, switchMap, NEVER, firstValueFrom, Subscription, isObservable, filter, debounceTime, BehaviorSubject } from 'rxjs';
5
- import { isPlatformBrowser, DOCUMENT, DatePipe, NgTemplateOutlet, LocationStrategy } from '@angular/common';
5
+ import { isPlatformBrowser, DOCUMENT, DatePipe, LocationStrategy, NgTemplateOutlet } from '@angular/common';
6
6
  import { BreakpointObserver } from '@angular/cdk/layout';
7
7
  import { Overlay } from '@angular/cdk/overlay';
8
8
  import { TemplatePortal } from '@angular/cdk/portal';
@@ -236,7 +236,11 @@ var en = {
236
236
  trigger: "Event",
237
237
  rule: "Automatic rule",
238
238
  files: "Attached files",
239
- "file-meta": "{{type}}, {{size}}"
239
+ "file-meta": "{{type}}, {{size}}",
240
+ application: "Application",
241
+ "application-label": "Message from the application",
242
+ "application-link": "Open",
243
+ "application-trusted": "Instruction for the assistant"
240
244
  },
241
245
  composer: {
242
246
  label: "Message",
@@ -354,6 +358,7 @@ var en = {
354
358
  "waiting-for-participant": "Waiting for the participant who started this task to decide.",
355
359
  "waiting-for-approver": "Waiting for an approver to decide.",
356
360
  "waiting-for-other-approver": "You started this work, so another approver decides.",
361
+ "waiting-for-asker": "Waiting for the person who asked to decide in their own chat.",
357
362
  "give-feedback": "What was wrong?",
358
363
  "stale-kind": {
359
364
  "already-done": "Already done",
@@ -361,7 +366,20 @@ var en = {
361
366
  "not-applicable": "No longer needed"
362
367
  },
363
368
  rationale: "Why: {{rationale}}",
364
- superseded: "Replaced by a newer proposal"
369
+ superseded: "Replaced by a newer proposal",
370
+ consultation: {
371
+ origin: "{{agent}} proposes this, about {{subject}}",
372
+ answer: "Answer of {{agent}}",
373
+ "own-rights": "Approving runs it right away with your own rights.",
374
+ leave: "Leave for approvers",
375
+ "leave-label": "Leave for approvers: {{summary}}",
376
+ "leave-explain": {
377
+ "subject-agent-rights": "The approvers of {{subject}} decide; it then runs with the agent's own rights.",
378
+ "subject-asker-rights": "The approvers of {{subject}} decide; it then runs with your rights."
379
+ },
380
+ "moved-status": "Left for approvers",
381
+ moved: "It now waits for the approvers of {{subject}}."
382
+ }
365
383
  },
366
384
  "proposal-feedback": {
367
385
  legend: "What was wrong? (optional)",
@@ -569,16 +587,21 @@ var en = {
569
587
  "assistant-consult-timeout": "The agent did not answer in time.",
570
588
  "assistant-consult-failed": "The agent could not answer.",
571
589
  "assistant-consult-read-only": "A consultation cannot change anything.",
590
+ "assistant-consult-proposal-refused": "No route at the subject takes this proposal any more. Approve or reject it here.",
591
+ "assistant-consult-asker-decides": "The person who asked decides this proposal in their own chat.",
592
+ "assistant-consult-route-not-allowed": "None of the agent's consultations allows this route, so it cannot be switched on.",
593
+ "assistant-consult-proposal-moved": "The proposal was left for the subject's approvers.",
572
594
  "assistant-moved-to-shadow": "The work went to the agent's shadow thread, where a run in shadow takes it.",
573
595
  "assistant-shadow-variant": "A shadow variant always works in shadow.",
574
596
  "assistant-service-account-not-used": "This agent does not answer to a technical account.",
575
597
  "assistant-service-account-not-found": "No account that may sign in exists with this id.",
576
598
  "assistant-skill-in-code": "This skill is part of the code and cannot change here.",
577
599
  "assistant-trigger-in-code": "This trigger is part of the code and cannot change here.",
600
+ "assistant-trigger-filter-not-found": "The event has no filter with this name. Choose from the filters the event offers.",
578
601
  "assistant-skill-in-use": "A trigger still starts this skill. Change or remove the trigger first.",
579
- "assistant-configuration-limit": "This agent already has as many skills or triggers as allowed.",
602
+ "assistant-configuration-limit": "This agent already has as many skills, triggers or consultations as allowed.",
580
603
  "assistant-run-settings-in-code": "This agent is part of the code; its runs are configured there.",
581
- "assistant-run-settings-in-use": "Skills or triggers still belong to this agent. Remove them first.",
604
+ "assistant-run-settings-in-use": "Skills, triggers or consultations still belong to this agent. Remove them first.",
582
605
  "assistant-skill-candidate-not-found": "This skill has no version being evaluated in shadow.",
583
606
  "assistant-skill-version-gone": "The skill version this run was for is no longer evaluated.",
584
607
  "assistant-account-search-unavailable": "The application offers no account search; enter the account's id.",
@@ -606,7 +629,10 @@ var en = {
606
629
  "assistant-file-type-not-accepted": "This type of file cannot be attached.",
607
630
  "assistant-file-upload-refused": "The application refused this file.",
608
631
  "assistant-file-upload-failed": "The file could not be stored right now. Try again in a moment.",
609
- "assistant-file-scan-refused": "The file did not pass the scan for harmful content and was not stored."
632
+ "assistant-file-scan-refused": "The file did not pass the scan for harmful content and was not stored.",
633
+ "assistant-consultation-in-code": "The agent allows consultation about this kind of object in code; it cannot change here.",
634
+ "assistant-consultation-not-found": "The consultation was not found.",
635
+ "assistant-consultations-not-allowed": "The application does not let administrators add consultations to this agent."
610
636
  },
611
637
  notices: {
612
638
  "no-answer": "The assistant finished without an answer. Try again or rephrase your question.",
@@ -764,6 +790,8 @@ var en = {
764
790
  changed: "Changed by {{user}} on {{date}}.",
765
791
  reason: "Note: {{reason}}",
766
792
  waiting: "{{count}} events wait for the resume.",
793
+ "stopped-was-off-hint": "It was off when it stopped: releasing the kill switch switches it back off. Resume switches it on.",
794
+ "stopped-was-on-hint": "It was not off when it stopped: releasing the kill switch makes it work again, as Resume does.",
767
795
  action: {
768
796
  active: "Resume",
769
797
  paused: "Pause",
@@ -851,6 +879,44 @@ var en = {
851
879
  },
852
880
  "input-label-search": "Account"
853
881
  },
882
+ "consultation-routes": {
883
+ title: "Consultation routes",
884
+ description: "Switch a route off for all consultations of this agent at once, in code and in the administration. When another assistant consults this agent and the agent proposes a change, a route decides who decides it and whose rights run it.",
885
+ "load-failed": "The consultation routes could not be read.",
886
+ route: {
887
+ "asker-decides": "The person who asked decides",
888
+ "subject-agent-rights": "Approvers decide, agent's rights",
889
+ "subject-asker-rights": "Approvers decide, asker's rights"
890
+ },
891
+ explain: {
892
+ "asker-decides": "The person who asked decides in their own chat; it runs with their own rights.",
893
+ "subject-agent-rights": "The subject's approvers other than the person who asked decide; it runs with the agent's own rights.",
894
+ "subject-asker-rights": "The subject's approvers decide; it runs later with the rights of the person who asked."
895
+ },
896
+ on: "On",
897
+ off: "Off",
898
+ code: "As the consultations allow",
899
+ overridden: "Overridden",
900
+ "not-allowed": "Not allowed",
901
+ "not-allowed-hint": "No consultation of this agent allows this route, so it stays off and cannot be switched on.",
902
+ "subject-types": "For: {{types}}",
903
+ action: {
904
+ on: "Switch on",
905
+ off: "Switch off",
906
+ code: "Back to what the consultations allow"
907
+ },
908
+ "explain-change": {
909
+ on: "Consultations use this route for new proposals again. Proposals already recorded keep their route.",
910
+ off: "Consultations no longer use this route for new proposals; with every route off, the agent's consultations only read. Proposals already recorded keep their route.",
911
+ "code-on": "The consultations decide again: one of them allows this route, so it is on.",
912
+ "code-off": "The consultations decide again: none of them allows this route, so it stays off."
913
+ },
914
+ confirm: {
915
+ on: "Confirm switching on",
916
+ off: "Confirm switching off",
917
+ code: "Confirm"
918
+ }
919
+ },
854
920
  "run-config": "Run configuration",
855
921
  "run-now": "Run now",
856
922
  runs: "Runs",
@@ -888,7 +954,7 @@ var en = {
888
954
  instructions: "Instructions",
889
955
  "instructions-hint": "What the agent does and how it answers.",
890
956
  autonomy: "Autonomy",
891
- "autonomy-hint": "Only Execute allows changes, and every change still needs approval.",
957
+ "autonomy-hint": "Only Execute allows changes in the agent's own work. In a consultation that may propose, Propose and Simulate propose changes too. Every change still needs approval.",
892
958
  "required-policy": "Required policy",
893
959
  "required-policy-hint": "Optional. Only users who pass this policy see the agent.",
894
960
  profile: "Model profile",
@@ -1308,7 +1374,8 @@ var en = {
1308
1374
  agent: "Run settings:",
1309
1375
  skill: "Skill {{id}}:",
1310
1376
  candidate: "Candidate of skill {{id}}:",
1311
- trigger: "Trigger {{id}}:"
1377
+ trigger: "Trigger {{id}}:",
1378
+ consultation: "Consultation about {{id}}:"
1312
1379
  },
1313
1380
  code: {
1314
1381
  "unknown-agent": "the agent can no longer run on its own.",
@@ -1319,11 +1386,23 @@ var en = {
1319
1386
  "unknown-policy": "the approval policy no longer exists.",
1320
1387
  "unknown-rule-handler": "the rules no longer exist.",
1321
1388
  "unknown-event": "the event no longer exists.",
1389
+ "unknown-filter": "a filter of the event no longer exists, so the trigger starts nothing until you remove it.",
1322
1390
  "unknown-subject-set": "the set of subjects no longer exists.",
1323
1391
  "unknown-skill": "the skill no longer exists.",
1324
1392
  "invalid-schedule": "the schedule or its time zone is not valid.",
1325
1393
  invalid: "a value is not valid.",
1326
- "not-worked-on": "it concerns a kind of object, or the whole application, that the agent does not work on."
1394
+ "not-worked-on": "it concerns a kind of object, or the whole application, that the agent does not work on.",
1395
+ "unknown-subject-type": "the kind of object no longer exists.",
1396
+ "unknown-agent-policy": "the policy the people who ask need is no longer listed.",
1397
+ "conflicts-with-tool": "another tool already has the name of its tool.",
1398
+ "not-consultable": "the application no longer lets administrators add consultations to this agent.",
1399
+ "route-not-allowed": "a route is no longer allowed, so it is left out.",
1400
+ "agent-answers-to-person": "the route with the agent's account is left out, because the agent answers to the object's responsible person.",
1401
+ "agent-has-no-rights": "the route with the agent's account is left out, because the agent answers for itself and has no rights of its own.",
1402
+ "no-approver": "the route with the agent's account is left out, because the approval policy lets only the person who caused a proposal decide it.",
1403
+ "limit-above-ceiling": "a limit is above what the application allows, so the application's applies.",
1404
+ "requests-not-allowed": "the application no longer allows requests, so it takes none.",
1405
+ "mcp-not-allowed": "the application no longer offers consultations to MCP clients, so they do not see it."
1327
1406
  }
1328
1407
  },
1329
1408
  "field-errors": {
@@ -1340,7 +1419,9 @@ var en = {
1340
1419
  "instructions-for-model": "Without fixed rules the model works from these instructions; describe what it should do.",
1341
1420
  "application-not-worked-on": "The agent does not work on the whole application. Choose a set of objects, or let the agent work on the whole application first.",
1342
1421
  "megabytes-up-to": "Enter a size above 0 and at most {{max}} MB.",
1343
- "megabytes-positive": "Enter a size above 0 MB."
1422
+ "megabytes-positive": "Enter a size above 0 MB.",
1423
+ "not-allowed": "The application does not allow this.",
1424
+ "routes-required": "Choose at least one route, or let it only answer."
1344
1425
  },
1345
1426
  settings: {
1346
1427
  title: "Run settings",
@@ -1551,6 +1632,13 @@ var en = {
1551
1632
  "source-schedule-hint": "At fixed times, for every subject in a set.",
1552
1633
  "source-schedule-application-hint": "At fixed times, for the whole application or for every object in a set.",
1553
1634
  "debounce-hint": "Waits this long for more events about the same subject and handles them in one run.",
1635
+ filters: {
1636
+ title: "Filters",
1637
+ hint: "Skip events this trigger should not start the agent on, such as changes the agent made itself. Every filter you select must let an event through.",
1638
+ empty: "This event offers no filters.",
1639
+ missing: "The event no longer offers this filter, so the trigger starts nothing. Clear it to save.",
1640
+ applied: "Filters: {{filters}}"
1641
+ },
1554
1642
  "subject-set-hint": "The projects or customers the schedule handles each time.",
1555
1643
  "message-hint": "What the agent reads as its task each time.",
1556
1644
  examples: "Examples",
@@ -1711,7 +1799,14 @@ var en = {
1711
1799
  "configured-off": "Run now: not while the application's code switches the agent off.",
1712
1800
  "no-settings": "Run now: only once the agent has run settings.",
1713
1801
  "no-catalog": "Run now: not until the run catalog can be read."
1714
- }
1802
+ },
1803
+ consultations: "When another assistant consults it",
1804
+ "no-consultations": "No other assistant can consult it yet.",
1805
+ "consultation-proposes": "Answers and proposes",
1806
+ "consultation-answers": "Only answers",
1807
+ "asked-by-none": "No agent asks it yet",
1808
+ "asked-by-one": "Asked by 1 agent",
1809
+ "asked-by": "Asked by {{count}} agents"
1715
1810
  },
1716
1811
  skills: {
1717
1812
  "code-rules": "Fixed rules in the application's code",
@@ -1794,7 +1889,124 @@ var en = {
1794
1889
  "edit-trigger": "Change the trigger",
1795
1890
  "edit-skill": "Change the skill",
1796
1891
  "edit-trigger-named": "Edit trigger {{trigger}}",
1797
- "edit-skill-named": "Edit skill {{skill}}"
1892
+ "edit-skill-named": "Edit skill {{skill}}",
1893
+ "edit-consultation": "Change the consultation",
1894
+ "edit-consultation-named": "Edit the consultation about {{type}}"
1895
+ }
1896
+ },
1897
+ consultations: {
1898
+ title: "Consultations",
1899
+ description: "Other assistants may consult this agent about an object. It answers with the rights of the person who asked and, where allowed, proposes changes. A consultation works only once an agent that asks selects its tool.",
1900
+ add: "Add consultation",
1901
+ empty: "Nobody can consult this agent yet.",
1902
+ closed: {
1903
+ application: "The application does not let administrators add consultations; they come from its code.",
1904
+ agent: "This agent is defined in code and does not let administrators add consultations to it.",
1905
+ "needs-settings": "Give the agent run settings before you add consultations.",
1906
+ limit: "This agent has as many consultations as allowed ({{max}}).",
1907
+ "no-types": "Every kind of object this agent works on already has a consultation."
1908
+ },
1909
+ requests: "Takes requests",
1910
+ mcp: "Offered to MCP clients",
1911
+ "no-description": "No description.",
1912
+ askers: "Asked by:",
1913
+ "open-asker": "Open the editor of {{agent}}",
1914
+ "no-askers": "No agent asks it yet. Select its tool in the tools of an agent that should ask it.",
1915
+ "code-hint": "Allowed in code; here only the route switches below change it.",
1916
+ "limits-value": "At most {{calls}} tool calls, {{seconds}} seconds, {{chars}} characters and {{history}} thread messages",
1917
+ "edit-named": "Edit the consultation about {{type}}",
1918
+ "remove-named": "Delete the consultation about {{type}}",
1919
+ "remove-explain": "Its tools disappear; proposals it made can still be decided.",
1920
+ added: "The consultation was added. It answers once an agent that asks selects its tool.",
1921
+ saved: "The consultation was saved.",
1922
+ removed: "The consultation was removed.",
1923
+ roles: {
1924
+ reader: "Reader",
1925
+ participant: "Participant",
1926
+ approver: "Approver"
1927
+ },
1928
+ "roles-lower": {
1929
+ reader: "reader",
1930
+ participant: "participant",
1931
+ approver: "approver"
1932
+ },
1933
+ summary: {
1934
+ ask: "People who are at least {{role}} of the object may ask.",
1935
+ "asker-policy": "They also need {{policy}}.",
1936
+ "asker-policy-agent": "They also need the policy the agent requires, or else access to the assistant.",
1937
+ "read-only": "The agent only answers and proposes nothing.",
1938
+ proposes: "The agent may propose changes when the person who asked is at least {{role}}: {{routes}}.",
1939
+ "proposes-code": "The agent may propose changes: {{routes}}."
1940
+ },
1941
+ routes: {
1942
+ "asker-decides": "The person who asked decides in their chat",
1943
+ "subject-asker-rights": "The object's approvers decide, with the rights of the person who asked",
1944
+ "subject-agent-rights": "The object's approvers decide, with this agent's technical account"
1945
+ },
1946
+ "routes-hint": {
1947
+ "asker-decides": "Approving runs it at once with that person's own rights.",
1948
+ "subject-asker-rights": "It waits at the object and runs later with the rights of the person who asked.",
1949
+ "subject-agent-rights": "It waits at the object for an approver other than the person who asked and runs with the agent's own rights."
1950
+ },
1951
+ "route-blocks": {
1952
+ "not-in-ceiling": "The application does not allow this route.",
1953
+ system: "Not for the whole application.",
1954
+ "agent-answers-to-person": "Not for an agent that answers to the object's responsible person.",
1955
+ "agent-has-no-rights": "Not for an agent that answers for itself: it has no rights of its own to lend."
1956
+ },
1957
+ limits: {
1958
+ "tool-calls": "Tool calls",
1959
+ "timeout-seconds": "Time limit in seconds",
1960
+ "answer-chars": "Longest answer in characters",
1961
+ "history-messages": "Thread messages it reads"
1962
+ },
1963
+ form: {
1964
+ "new-title": "New consultation",
1965
+ "edit-title": "Consultation about {{type}}, version {{version}}",
1966
+ what: "What may other assistants consult it about?",
1967
+ "subject-type": "Kind of object",
1968
+ "subject-type-hint": "Only kinds of objects the agent works on and has no consultation about yet.",
1969
+ description: "When to consult it",
1970
+ "description-hint": "The assistant that asks reads this to decide when to consult this agent.",
1971
+ instructions: "Instructions when consulted",
1972
+ "instructions-hint": "Optional. What the agent keeps in mind when it answers, after the platform's own rules for consultations.",
1973
+ who: "Who may ask?",
1974
+ "minimum-role": "The person who asks is at least",
1975
+ "role-hint": {
1976
+ reader: "Anyone who may read the object.",
1977
+ participant: "People who work on the object.",
1978
+ approver: "People who approve changes to the object."
1979
+ },
1980
+ "asker-policy": "Policy the person needs",
1981
+ "asker-policy-agent": "The policy the agent requires",
1982
+ "asker-policy-hint": "One of the policies the application lists. Without one, the agent's own policy applies, or else access to the assistant.",
1983
+ "do": "What may it do?",
1984
+ answer: "Only answer",
1985
+ "answer-hint": "It answers with the rights of the person who asked and proposes nothing.",
1986
+ propose: "Answer and propose changes",
1987
+ "propose-hint": "It may also propose changes; the routes below decide who decides them.",
1988
+ "answers-only": "The application lets consultations only answer.",
1989
+ routes: "Who decides its proposals?",
1990
+ "routes-hint": "Choose at least one. Of the two routes at the object, choose one at most.",
1991
+ "proposal-role": "Role needed for proposals",
1992
+ "proposal-role-hint": "The person who asked needs at least this role on the object before the agent may propose; with a lower role they get the answer without proposals.",
1993
+ "agent-policy": "The agent's approval policy",
1994
+ "policy-warning": {
1995
+ "four-eyes": "This approval policy uses four eyes, so the person who asked cannot decide their own proposal in their chat; that route does not apply.",
1996
+ "initiator-only": "This approval policy lets only the person who caused a proposal decide it, so the route with the agent's technical account is refused."
1997
+ },
1998
+ advanced: "Advanced: limits, requests and classification",
1999
+ limits: "Limits of one consultation",
2000
+ "limits-hint": "Empty starts at the default within what the application allows.",
2001
+ "limit-range": "From {{min}} to {{max}}.",
2002
+ requests: "Also take requests",
2003
+ "requests-hint": "The assistant that asks may also ask this agent to start a run, with the person as its initiator.",
2004
+ "requests-closed": "The application does not allow requests.",
2005
+ mcp: "Offer to MCP clients",
2006
+ "mcp-hint": "External MCP clients may consult it too, with the rights of their person.",
2007
+ "mcp-closed": "The application does not offer consultations to MCP clients.",
2008
+ "classification-hint": "How sensitive its answers are in the conversation that asked. Without one, the agent's classification applies.",
2009
+ "in-effect": "A consultation is in effect once saved; it answers once an agent that asks selects its tool."
1798
2010
  }
1799
2011
  }
1800
2012
  },
@@ -1933,6 +2145,7 @@ var en = {
1933
2145
  inbox: {
1934
2146
  title: "Proposals to decide",
1935
2147
  description: "Proposals of agents that wait for your decision, the ones that expire first on top.",
2148
+ "description-grouped": "Proposals of agents that wait for your decision, grouped by object; the group with the proposal that expires first is on top.",
1936
2149
  empty: "No proposals wait for you.",
1937
2150
  select: "Select this proposal",
1938
2151
  "select-all": "Select all",
@@ -1941,7 +2154,18 @@ var en = {
1941
2154
  previous: "Previous",
1942
2155
  next: "Next",
1943
2156
  page: "Page {{page}} of {{pages}}",
1944
- pages: "Pages"
2157
+ pages: "Pages",
2158
+ "new": "New",
2159
+ "new-label": "New since your last visit",
2160
+ "last-visit": "You last looked here on {{date}}. What came since is marked new.",
2161
+ "first-visit": "Everything here is new for you.",
2162
+ "group-loaded": "{{loaded}} of {{total}} shown",
2163
+ "select-group": "Select the {{count}} shown",
2164
+ "select-group-label": "Select the {{count}} proposals of {{name}} shown on this page",
2165
+ "group-continued": "Continued from the previous page.",
2166
+ "group-continues": "More proposals of this group are on the next page.",
2167
+ run: "One run of {{agent}}, {{date}}",
2168
+ "no-run": "Proposed in a conversation"
1945
2169
  },
1946
2170
  proposal: {
1947
2171
  approve: "Approve",
@@ -1971,7 +2195,22 @@ var en = {
1971
2195
  shadow: "Shadow"
1972
2196
  },
1973
2197
  "from-consultation": "From a consultation",
1974
- "from-consultation-hint": "Another assistant consulted the agent for this person; it runs with that person's rights only.",
2198
+ "from-consultation-hint": "Another assistant consulted the agent for a person, and the agent proposed this.",
2199
+ consultation: {
2200
+ "asked-by": "Asked by {{name}}",
2201
+ "the-asker": "the person who asked",
2202
+ route: {
2203
+ "asker-decides": "The person who asked decides in their own chat; it runs with their own rights.",
2204
+ "subject-agent-rights": "The subject's approvers other than the person who asked decide; it runs with the agent's own rights.",
2205
+ "subject-asker-rights": "The subject's approvers decide; it runs with the rights of the person who asked."
2206
+ },
2207
+ "waiting-for-asker": "Waiting for {{name}} to decide in their own chat.",
2208
+ "four-eyes": "You asked for this, so another approver decides; it runs with the agent's own rights.",
2209
+ "runs-with": {
2210
+ agent: "Runs with the agent's rights",
2211
+ accountable: "Runs with the technical account's rights"
2212
+ }
2213
+ },
1975
2214
  "give-feedback": "Give feedback",
1976
2215
  "change-feedback": "Change feedback",
1977
2216
  "your-feedback": "Your feedback: {{category}}",
@@ -2050,6 +2289,11 @@ var en = {
2050
2289
  running: "Running",
2051
2290
  completed: "Answered",
2052
2291
  failed: "Not answered"
2292
+ },
2293
+ routes: {
2294
+ "asker-decides": "Asker decides: {{count}}",
2295
+ "subject-agent-rights": "Approvers, agent's rights: {{count}}",
2296
+ "subject-asker-rights": "Approvers, asker's rights: {{count}}"
2053
2297
  }
2054
2298
  },
2055
2299
  "object-agents": {
@@ -2254,7 +2498,11 @@ var nl = {
2254
2498
  trigger: "Gebeurtenis",
2255
2499
  rule: "Automatische regel",
2256
2500
  files: "Bijgevoegde bestanden",
2257
- "file-meta": "{{type}}, {{size}}"
2501
+ "file-meta": "{{type}}, {{size}}",
2502
+ application: "Applicatie",
2503
+ "application-label": "Bericht van de applicatie",
2504
+ "application-link": "Openen",
2505
+ "application-trusted": "Instructie voor de assistent"
2258
2506
  },
2259
2507
  composer: {
2260
2508
  label: "Bericht",
@@ -2372,6 +2620,7 @@ var nl = {
2372
2620
  "waiting-for-participant": "Wacht op de beslissing van de deelnemer die deze taak startte.",
2373
2621
  "waiting-for-approver": "Wacht tot een beslisser hierover beslist.",
2374
2622
  "waiting-for-other-approver": "Jij startte dit werk, dus een andere beslisser beslist hierover.",
2623
+ "waiting-for-asker": "Wacht tot degene die het vroeg, beslist in de eigen chat.",
2375
2624
  "give-feedback": "Wat klopte er niet?",
2376
2625
  "stale-kind": {
2377
2626
  "already-done": "Al gedaan",
@@ -2379,7 +2628,20 @@ var nl = {
2379
2628
  "not-applicable": "Niet meer nodig"
2380
2629
  },
2381
2630
  rationale: "Waarom: {{rationale}}",
2382
- superseded: "Vervangen door een nieuwer voorstel"
2631
+ superseded: "Vervangen door een nieuwer voorstel",
2632
+ consultation: {
2633
+ origin: "{{agent}} stelt dit voor, over {{subject}}",
2634
+ answer: "Antwoord van {{agent}}",
2635
+ "own-rights": "Goedkeuren voert het meteen uit met je eigen rechten.",
2636
+ leave: "Aan goedkeurders laten",
2637
+ "leave-label": "Aan goedkeurders laten: {{summary}}",
2638
+ "leave-explain": {
2639
+ "subject-agent-rights": "De goedkeurders van {{subject}} beslissen; daarna loopt het met de eigen rechten van de agent.",
2640
+ "subject-asker-rights": "De goedkeurders van {{subject}} beslissen; daarna loopt het met jouw rechten."
2641
+ },
2642
+ "moved-status": "Aan goedkeurders gelaten",
2643
+ moved: "Het wacht nu op de goedkeurders van {{subject}}."
2644
+ }
2383
2645
  },
2384
2646
  "proposal-feedback": {
2385
2647
  legend: "Wat klopte er niet? (optioneel)",
@@ -2587,16 +2849,21 @@ var nl = {
2587
2849
  "assistant-consult-timeout": "De agent antwoordde niet op tijd.",
2588
2850
  "assistant-consult-failed": "De agent kon niet antwoorden.",
2589
2851
  "assistant-consult-read-only": "Een raadpleging kan niets wijzigen.",
2852
+ "assistant-consult-proposal-refused": "Geen route bij het onderwerp neemt dit voorstel nog aan. Keur het hier goed of af.",
2853
+ "assistant-consult-asker-decides": "Degene die het vroeg, beslist over dit voorstel in de eigen chat.",
2854
+ "assistant-consult-route-not-allowed": "Geen van de raadplegingen van de agent staat deze route toe, dus die kan niet aan.",
2855
+ "assistant-consult-proposal-moved": "Het voorstel is aan de goedkeurders van het onderwerp gelaten.",
2590
2856
  "assistant-moved-to-shadow": "Het werk is naar de schaduwthread van de agent gegaan, waar een run in schaduw het oppakt.",
2591
2857
  "assistant-shadow-variant": "Een schaduwvariant werkt altijd in schaduw.",
2592
2858
  "assistant-service-account-not-used": "Deze agent werkt niet onder een technisch account.",
2593
2859
  "assistant-service-account-not-found": "Er bestaat geen account met deze id dat mag inloggen.",
2594
2860
  "assistant-skill-in-code": "Deze vaardigheid staat in de code en kan hier niet veranderen.",
2595
2861
  "assistant-trigger-in-code": "Deze trigger staat in de code en kan hier niet veranderen.",
2862
+ "assistant-trigger-filter-not-found": "De gebeurtenis heeft geen filter met deze naam. Kies uit de filters die de gebeurtenis biedt.",
2596
2863
  "assistant-skill-in-use": "Een trigger start deze vaardigheid nog. Pas de trigger eerst aan of verwijder hem.",
2597
- "assistant-configuration-limit": "Deze agent heeft al zoveel vaardigheden of triggers als is toegestaan.",
2864
+ "assistant-configuration-limit": "Deze agent heeft al zoveel vaardigheden, triggers of raadplegingen als is toegestaan.",
2598
2865
  "assistant-run-settings-in-code": "Deze agent staat in de code; daar is ingesteld hoe hij werkt.",
2599
- "assistant-run-settings-in-use": "Er horen nog vaardigheden of triggers bij deze agent. Verwijder die eerst.",
2866
+ "assistant-run-settings-in-use": "Er horen nog vaardigheden, triggers of raadplegingen bij deze agent. Verwijder die eerst.",
2600
2867
  "assistant-skill-candidate-not-found": "Van deze vaardigheid wordt geen versie in schaduw beoordeeld.",
2601
2868
  "assistant-skill-version-gone": "De versie van de vaardigheid waarvoor deze run was, wordt niet meer beoordeeld.",
2602
2869
  "assistant-account-search-unavailable": "De applicatie biedt geen accountzoeker; vul de id van het account in.",
@@ -2624,7 +2891,10 @@ var nl = {
2624
2891
  "assistant-file-type-not-accepted": "Dit type bestand kan niet worden bijgevoegd.",
2625
2892
  "assistant-file-upload-refused": "De applicatie heeft dit bestand geweigerd.",
2626
2893
  "assistant-file-upload-failed": "Het bestand kon nu niet worden opgeslagen. Probeer het zo opnieuw.",
2627
- "assistant-file-scan-refused": "Het bestand is niet door de scan op schadelijke inhoud gekomen en is niet opgeslagen."
2894
+ "assistant-file-scan-refused": "Het bestand is niet door de scan op schadelijke inhoud gekomen en is niet opgeslagen.",
2895
+ "assistant-consultation-in-code": "De agent staat raadpleging over deze soort object in code toe; die kan hier niet veranderen.",
2896
+ "assistant-consultation-not-found": "De raadpleging is niet gevonden.",
2897
+ "assistant-consultations-not-allowed": "De applicatie laat beheerders geen raadplegingen aan deze agent toevoegen."
2628
2898
  },
2629
2899
  notices: {
2630
2900
  "no-answer": "De assistent is klaar zonder antwoord. Probeer het opnieuw of stel je vraag anders.",
@@ -2782,6 +3052,8 @@ var nl = {
2782
3052
  changed: "Gewijzigd door {{user}} op {{date}}.",
2783
3053
  reason: "Notitie: {{reason}}",
2784
3054
  waiting: "{{count}} gebeurtenissen wachten op het hervatten.",
3055
+ "stopped-was-off-hint": "Hij stond uit toen hij stopte: de noodstop opheffen zet hem weer uit. Hervatten zet hem aan.",
3056
+ "stopped-was-on-hint": "Hij stond niet uit toen hij stopte: de noodstop opheffen laat hem weer werken, net als Hervatten.",
2785
3057
  action: {
2786
3058
  active: "Hervatten",
2787
3059
  paused: "Pauzeren",
@@ -2869,6 +3141,44 @@ var nl = {
2869
3141
  },
2870
3142
  "input-label-search": "Account"
2871
3143
  },
3144
+ "consultation-routes": {
3145
+ title: "Routes van raadplegingen",
3146
+ description: "Zet een route in één keer uit voor alle raadplegingen van deze agent, in code en in het beheer. Wanneer een andere assistent deze agent raadpleegt en de agent een wijziging voorstelt, bepaalt een route wie erover beslist en met wiens rechten het loopt.",
3147
+ "load-failed": "De routes van raadplegingen konden niet worden gelezen.",
3148
+ route: {
3149
+ "asker-decides": "Degene die het vroeg, beslist",
3150
+ "subject-agent-rights": "Goedkeurders beslissen, rechten van de agent",
3151
+ "subject-asker-rights": "Goedkeurders beslissen, rechten van de vrager"
3152
+ },
3153
+ explain: {
3154
+ "asker-decides": "Degene die het vroeg, beslist in de eigen chat; het loopt met diens eigen rechten.",
3155
+ "subject-agent-rights": "De goedkeurders van het onderwerp, behalve degene die het vroeg, beslissen; het loopt met de eigen rechten van de agent.",
3156
+ "subject-asker-rights": "De goedkeurders van het onderwerp beslissen; het loopt later met de rechten van degene die het vroeg."
3157
+ },
3158
+ on: "Aan",
3159
+ off: "Uit",
3160
+ code: "Zoals de raadplegingen toestaan",
3161
+ overridden: "Overschreven",
3162
+ "not-allowed": "Niet toegestaan",
3163
+ "not-allowed-hint": "Geen raadpleging van deze agent staat deze route toe, dus die blijft uit en kan niet aan.",
3164
+ "subject-types": "Voor: {{types}}",
3165
+ action: {
3166
+ on: "Aanzetten",
3167
+ off: "Uitzetten",
3168
+ code: "Terug naar wat de raadplegingen toestaan"
3169
+ },
3170
+ "explain-change": {
3171
+ on: "Raadplegingen gebruiken deze route weer voor nieuwe voorstellen. Voorstellen die er al zijn, houden hun route.",
3172
+ off: "Raadplegingen gebruiken deze route niet meer voor nieuwe voorstellen; met alle routes uit lezen de raadplegingen van de agent alleen. Voorstellen die er al zijn, houden hun route.",
3173
+ "code-on": "De raadplegingen beslissen weer: een ervan staat deze route toe, dus die staat aan.",
3174
+ "code-off": "De raadplegingen beslissen weer: geen ervan staat deze route toe, dus die blijft uit."
3175
+ },
3176
+ confirm: {
3177
+ on: "Aanzetten bevestigen",
3178
+ off: "Uitzetten bevestigen",
3179
+ code: "Bevestigen"
3180
+ }
3181
+ },
2872
3182
  "run-config": "Runconfiguratie",
2873
3183
  "run-now": "Nu uitvoeren",
2874
3184
  runs: "Runs",
@@ -2906,7 +3216,7 @@ var nl = {
2906
3216
  instructions: "Instructies",
2907
3217
  "instructions-hint": "Wat de agent doet en hoe hij antwoordt.",
2908
3218
  autonomy: "Autonomie",
2909
- "autonomy-hint": "Alleen Uitvoeren staat wijzigingen toe, en elke wijziging vraagt nog steeds om goedkeuring.",
3219
+ "autonomy-hint": "Alleen Uitvoeren staat wijzigingen toe in het eigen werk van de agent. In een raadpleging die mag voorstellen, stellen Voorstellen en Simuleren ook wijzigingen voor. Elke wijziging vraagt nog steeds om goedkeuring.",
2910
3220
  "required-policy": "Vereiste policy",
2911
3221
  "required-policy-hint": "Optioneel. Alleen gebruikers die aan deze policy voldoen zien de agent.",
2912
3222
  profile: "Modelprofiel",
@@ -3326,7 +3636,8 @@ var nl = {
3326
3636
  agent: "Runinstellingen:",
3327
3637
  skill: "Vaardigheid {{id}}:",
3328
3638
  candidate: "Kandidaat van vaardigheid {{id}}:",
3329
- trigger: "Trigger {{id}}:"
3639
+ trigger: "Trigger {{id}}:",
3640
+ consultation: "Raadpleging over {{id}}:"
3330
3641
  },
3331
3642
  code: {
3332
3643
  "unknown-agent": "de agent kan niet meer zelfstandig werken.",
@@ -3337,11 +3648,23 @@ var nl = {
3337
3648
  "unknown-policy": "het goedkeuringsbeleid bestaat niet meer.",
3338
3649
  "unknown-rule-handler": "de regels bestaan niet meer.",
3339
3650
  "unknown-event": "de gebeurtenis bestaat niet meer.",
3651
+ "unknown-filter": "een filter van de gebeurtenis bestaat niet meer, dus de trigger start niets totdat je het verwijdert.",
3340
3652
  "unknown-subject-set": "de set onderwerpen bestaat niet meer.",
3341
3653
  "unknown-skill": "de vaardigheid bestaat niet meer.",
3342
3654
  "invalid-schedule": "het schema of de tijdzone is niet geldig.",
3343
3655
  invalid: "een waarde is niet geldig.",
3344
- "not-worked-on": "het gaat over een soort object, of de hele applicatie, waaraan de agent niet werkt."
3656
+ "not-worked-on": "het gaat over een soort object, of de hele applicatie, waaraan de agent niet werkt.",
3657
+ "unknown-subject-type": "de soort object bestaat niet meer.",
3658
+ "unknown-agent-policy": "het beleid dat vragers nodig hebben, wordt niet meer aangeboden.",
3659
+ "conflicts-with-tool": "een andere tool heeft al de naam van haar tool.",
3660
+ "not-consultable": "de applicatie laat beheerders geen raadplegingen meer aan deze agent toevoegen.",
3661
+ "route-not-allowed": "een route is niet meer toegestaan en valt weg.",
3662
+ "agent-answers-to-person": "de route met het account van de agent valt weg, omdat de agent verantwoording aflegt aan de verantwoordelijke van het object.",
3663
+ "agent-has-no-rights": "de route met het account van de agent valt weg, omdat de agent voor zichzelf antwoordt en geen eigen rechten heeft.",
3664
+ "no-approver": "de route met het account van de agent valt weg, omdat het goedkeuringsbeleid alleen degene die een voorstel veroorzaakte laat beslissen.",
3665
+ "limit-above-ceiling": "een grens ligt boven wat de applicatie toestaat, dus die van de applicatie geldt.",
3666
+ "requests-not-allowed": "de applicatie staat geen verzoeken meer toe, dus ze neemt er geen aan.",
3667
+ "mcp-not-allowed": "de applicatie biedt raadplegingen niet meer aan MCP-clients aan, dus die zien haar niet."
3345
3668
  }
3346
3669
  },
3347
3670
  "field-errors": {
@@ -3358,7 +3681,9 @@ var nl = {
3358
3681
  "instructions-for-model": "Zonder vaste regels werkt het model met deze instructies; beschrijf wat het moet doen.",
3359
3682
  "application-not-worked-on": "De agent werkt niet aan de hele applicatie. Kies een verzameling objecten, of laat de agent eerst aan de hele applicatie werken.",
3360
3683
  "megabytes-up-to": "Vul een grootte in boven 0 en hooguit {{max}} MB.",
3361
- "megabytes-positive": "Vul een grootte in boven 0 MB."
3684
+ "megabytes-positive": "Vul een grootte in boven 0 MB.",
3685
+ "not-allowed": "De applicatie staat dit niet toe.",
3686
+ "routes-required": "Kies minstens één route, of laat hem alleen antwoorden."
3362
3687
  },
3363
3688
  settings: {
3364
3689
  title: "Runinstellingen",
@@ -3569,6 +3894,13 @@ var nl = {
3569
3894
  "source-schedule-hint": "Op vaste momenten, voor elk onderwerp in een verzameling.",
3570
3895
  "source-schedule-application-hint": "Op vaste momenten, voor de hele applicatie of voor elk object in een verzameling.",
3571
3896
  "debounce-hint": "Wacht zo lang op meer gebeurtenissen over hetzelfde onderwerp en behandelt ze in één run.",
3897
+ filters: {
3898
+ title: "Filters",
3899
+ hint: "Sla gebeurtenissen over waarop deze trigger de agent niet moet starten, zoals wijzigingen die de agent zelf deed. Elk filter dat je kiest moet een gebeurtenis doorlaten.",
3900
+ empty: "Deze gebeurtenis biedt geen filters.",
3901
+ missing: "De gebeurtenis biedt dit filter niet meer, dus de trigger start niets. Haal het weg om op te slaan.",
3902
+ applied: "Filters: {{filters}}"
3903
+ },
3572
3904
  "subject-set-hint": "De projecten of klanten die het schema telkens behandelt.",
3573
3905
  "message-hint": "Wat de agent telkens als opdracht leest.",
3574
3906
  examples: "Voorbeelden",
@@ -3729,7 +4061,14 @@ var nl = {
3729
4061
  "configured-off": "Nu uitvoeren: niet zolang de code van de applicatie de agent uitschakelt.",
3730
4062
  "no-settings": "Nu uitvoeren: pas als de agent runinstellingen heeft.",
3731
4063
  "no-catalog": "Nu uitvoeren: pas als de runcatalogus gelezen kan worden."
3732
- }
4064
+ },
4065
+ consultations: "Als een andere assistent hem raadpleegt",
4066
+ "no-consultations": "Nog geen andere assistent kan hem raadplegen.",
4067
+ "consultation-proposes": "Antwoordt en stelt voor",
4068
+ "consultation-answers": "Antwoordt alleen",
4069
+ "asked-by-none": "Nog geen agent vraagt het",
4070
+ "asked-by-one": "Gevraagd door 1 agent",
4071
+ "asked-by": "Gevraagd door {{count}} agents"
3733
4072
  },
3734
4073
  skills: {
3735
4074
  "code-rules": "Vaste regels in de code van de applicatie",
@@ -3812,7 +4151,124 @@ var nl = {
3812
4151
  "edit-trigger": "Trigger wijzigen",
3813
4152
  "edit-skill": "Vaardigheid wijzigen",
3814
4153
  "edit-trigger-named": "Trigger {{trigger}} bewerken",
3815
- "edit-skill-named": "Vaardigheid {{skill}} bewerken"
4154
+ "edit-skill-named": "Vaardigheid {{skill}} bewerken",
4155
+ "edit-consultation": "Raadpleging wijzigen",
4156
+ "edit-consultation-named": "De raadpleging over {{type}} bewerken"
4157
+ }
4158
+ },
4159
+ consultations: {
4160
+ title: "Raadplegingen",
4161
+ description: "Andere assistenten mogen deze agent raadplegen over een object. Hij antwoordt met de rechten van degene die het vroeg en stelt, waar dat mag, wijzigingen voor. Een raadpleging werkt pas als een agent die vraagt haar tool selecteert.",
4162
+ add: "Raadpleging toevoegen",
4163
+ empty: "Niemand kan deze agent nog raadplegen.",
4164
+ closed: {
4165
+ application: "De applicatie laat beheerders geen raadplegingen toevoegen; die komen uit haar code.",
4166
+ agent: "Deze agent staat in de code en laat beheerders geen raadplegingen toevoegen.",
4167
+ "needs-settings": "Geef de agent runinstellingen voordat je raadplegingen toevoegt.",
4168
+ limit: "Deze agent heeft al zoveel raadplegingen als is toegestaan ({{max}}).",
4169
+ "no-types": "Elke soort object waaraan deze agent werkt, heeft al een raadpleging."
4170
+ },
4171
+ requests: "Neemt verzoeken aan",
4172
+ mcp: "Aangeboden aan MCP-clients",
4173
+ "no-description": "Geen beschrijving.",
4174
+ askers: "Gevraagd door:",
4175
+ "open-asker": "De editor van {{agent}} openen",
4176
+ "no-askers": "Nog geen agent vraagt het. Selecteer de tool bij de tools van een agent die het moet vragen.",
4177
+ "code-hint": "Toegestaan in code; hier veranderen alleen de routeschakelaars hieronder iets.",
4178
+ "limits-value": "Hooguit {{calls}} toolaanroepen, {{seconds}} seconden, {{chars}} tekens en {{history}} berichten uit de thread",
4179
+ "edit-named": "De raadpleging over {{type}} bewerken",
4180
+ "remove-named": "De raadpleging over {{type}} verwijderen",
4181
+ "remove-explain": "Haar tools verdwijnen; voorstellen die ze deed, kunnen nog worden beslist.",
4182
+ added: "De raadpleging is toegevoegd. Ze antwoordt zodra een agent die vraagt haar tool selecteert.",
4183
+ saved: "De raadpleging is opgeslagen.",
4184
+ removed: "De raadpleging is verwijderd.",
4185
+ roles: {
4186
+ reader: "Lezer",
4187
+ participant: "Deelnemer",
4188
+ approver: "Goedkeurder"
4189
+ },
4190
+ "roles-lower": {
4191
+ reader: "lezer",
4192
+ participant: "deelnemer",
4193
+ approver: "goedkeurder"
4194
+ },
4195
+ summary: {
4196
+ ask: "Mensen die minstens {{role}} van het object zijn, mogen het vragen.",
4197
+ "asker-policy": "Ze hebben ook {{policy}} nodig.",
4198
+ "asker-policy-agent": "Ze hebben ook het beleid nodig dat de agent vereist, of anders toegang tot de assistent.",
4199
+ "read-only": "De agent antwoordt alleen en stelt niets voor.",
4200
+ proposes: "De agent mag wijzigingen voorstellen als degene die het vroeg minstens {{role}} is: {{routes}}.",
4201
+ "proposes-code": "De agent mag wijzigingen voorstellen: {{routes}}."
4202
+ },
4203
+ routes: {
4204
+ "asker-decides": "Degene die het vroeg, beslist in de eigen chat",
4205
+ "subject-asker-rights": "De goedkeurders van het object beslissen, met de rechten van degene die het vroeg",
4206
+ "subject-agent-rights": "De goedkeurders van het object beslissen, met het technische account van deze agent"
4207
+ },
4208
+ "routes-hint": {
4209
+ "asker-decides": "Goedkeuren voert het meteen uit met diens eigen rechten.",
4210
+ "subject-asker-rights": "Het wacht bij het object en loopt later met de rechten van degene die het vroeg.",
4211
+ "subject-agent-rights": "Het wacht bij het object op een goedkeurder anders dan degene die het vroeg en loopt met de eigen rechten van de agent."
4212
+ },
4213
+ "route-blocks": {
4214
+ "not-in-ceiling": "De applicatie staat deze route niet toe.",
4215
+ system: "Niet voor de hele applicatie.",
4216
+ "agent-answers-to-person": "Niet voor een agent die verantwoording aflegt aan de verantwoordelijke van het object.",
4217
+ "agent-has-no-rights": "Niet voor een agent die voor zichzelf antwoordt: hij heeft geen eigen rechten om uit te lenen."
4218
+ },
4219
+ limits: {
4220
+ "tool-calls": "Toolaanroepen",
4221
+ "timeout-seconds": "Tijdslimiet in seconden",
4222
+ "answer-chars": "Langste antwoord in tekens",
4223
+ "history-messages": "Berichten uit de thread die hij leest"
4224
+ },
4225
+ form: {
4226
+ "new-title": "Nieuwe raadpleging",
4227
+ "edit-title": "Raadpleging over {{type}}, versie {{version}}",
4228
+ what: "Waarover mogen andere assistenten hem raadplegen?",
4229
+ "subject-type": "Soort object",
4230
+ "subject-type-hint": "Alleen soorten objecten waaraan de agent werkt en waarover hij nog geen raadpleging heeft.",
4231
+ description: "Wanneer je hem raadpleegt",
4232
+ "description-hint": "De assistent die vraagt, leest dit om te bepalen wanneer hij deze agent raadpleegt.",
4233
+ instructions: "Instructies bij een raadpleging",
4234
+ "instructions-hint": "Optioneel. Waar de agent op let als hij antwoordt, na de eigen regels van het platform voor raadplegingen.",
4235
+ who: "Wie mag het vragen?",
4236
+ "minimum-role": "Degene die het vraagt is minstens",
4237
+ "role-hint": {
4238
+ reader: "Iedereen die het object mag lezen.",
4239
+ participant: "Mensen die aan het object werken.",
4240
+ approver: "Mensen die wijzigingen aan het object goedkeuren."
4241
+ },
4242
+ "asker-policy": "Beleid dat de persoon nodig heeft",
4243
+ "asker-policy-agent": "Het beleid dat de agent vereist",
4244
+ "asker-policy-hint": "Een van de beleidsregels die de applicatie aanbiedt. Zonder geldt het eigen beleid van de agent, of anders toegang tot de assistent.",
4245
+ "do": "Wat mag hij doen?",
4246
+ answer: "Alleen antwoorden",
4247
+ "answer-hint": "Hij antwoordt met de rechten van degene die het vroeg en stelt niets voor.",
4248
+ propose: "Antwoorden en wijzigingen voorstellen",
4249
+ "propose-hint": "Hij mag ook wijzigingen voorstellen; de routes hieronder bepalen wie erover beslist.",
4250
+ "answers-only": "De applicatie laat raadplegingen alleen antwoorden.",
4251
+ routes: "Wie beslist over zijn voorstellen?",
4252
+ "routes-hint": "Kies er minstens één. Van de twee routes bij het object kies je er hooguit één.",
4253
+ "proposal-role": "Rol nodig voor voorstellen",
4254
+ "proposal-role-hint": "Degene die het vroeg heeft minstens deze rol op het object nodig voordat de agent iets mag voorstellen; met een lagere rol krijgt die het antwoord zonder voorstellen.",
4255
+ "agent-policy": "Het goedkeuringsbeleid van de agent",
4256
+ "policy-warning": {
4257
+ "four-eyes": "Dit goedkeuringsbeleid vraagt vier ogen, dus degene die het vroeg kan het eigen voorstel niet in de eigen chat beslissen; die route geldt dan niet.",
4258
+ "initiator-only": "Dit goedkeuringsbeleid laat alleen degene die een voorstel veroorzaakte erover beslissen, dus de route met het technische account van de agent wordt geweigerd."
4259
+ },
4260
+ advanced: "Geavanceerd: grenzen, verzoeken en classificatie",
4261
+ limits: "Grenzen van één raadpleging",
4262
+ "limits-hint": "Leeg begint bij de standaard binnen wat de applicatie toestaat.",
4263
+ "limit-range": "Van {{min}} tot en met {{max}}.",
4264
+ requests: "Ook verzoeken aannemen",
4265
+ "requests-hint": "De assistent die vraagt, mag deze agent ook vragen een run te starten, met de persoon als initiatiefnemer.",
4266
+ "requests-closed": "De applicatie staat geen verzoeken toe.",
4267
+ mcp: "Aanbieden aan MCP-clients",
4268
+ "mcp-hint": "Externe MCP-clients mogen hem ook raadplegen, met de rechten van hun persoon.",
4269
+ "mcp-closed": "De applicatie biedt raadplegingen niet aan MCP-clients aan.",
4270
+ "classification-hint": "Hoe gevoelig zijn antwoorden zijn in het gesprek dat vroeg. Zonder geldt de classificatie van de agent.",
4271
+ "in-effect": "Een raadpleging werkt zodra ze is opgeslagen; ze antwoordt zodra een agent die vraagt haar tool selecteert."
3816
4272
  }
3817
4273
  }
3818
4274
  },
@@ -3951,6 +4407,7 @@ var nl = {
3951
4407
  inbox: {
3952
4408
  title: "Voorstellen om te beslissen",
3953
4409
  description: "Voorstellen van agents die op jouw besluit wachten, de eerst verlopende bovenaan.",
4410
+ "description-grouped": "Voorstellen van agents die op jouw besluit wachten, gegroepeerd per object; de groep met het eerst verlopende voorstel staat bovenaan.",
3954
4411
  empty: "Er wachten geen voorstellen op jou.",
3955
4412
  select: "Dit voorstel selecteren",
3956
4413
  "select-all": "Alles selecteren",
@@ -3959,7 +4416,18 @@ var nl = {
3959
4416
  previous: "Vorige",
3960
4417
  next: "Volgende",
3961
4418
  page: "Pagina {{page}} van {{pages}}",
3962
- pages: "Pagina's"
4419
+ pages: "Pagina's",
4420
+ "new": "Nieuw",
4421
+ "new-label": "Nieuw sinds je laatste bezoek",
4422
+ "last-visit": "Je keek hier voor het laatst op {{date}}. Wat sindsdien kwam, staat als nieuw gemarkeerd.",
4423
+ "first-visit": "Alles hier is nieuw voor je.",
4424
+ "group-loaded": "{{loaded}} van {{total}} getoond",
4425
+ "select-group": "De {{count}} getoonde selecteren",
4426
+ "select-group-label": "De {{count}} voorstellen van {{name}} op deze pagina selecteren",
4427
+ "group-continued": "Vervolg van de vorige pagina.",
4428
+ "group-continues": "Meer voorstellen van deze groep staan op de volgende pagina.",
4429
+ run: "Eén run van {{agent}}, {{date}}",
4430
+ "no-run": "Voorgesteld in een gesprek"
3963
4431
  },
3964
4432
  proposal: {
3965
4433
  approve: "Goedkeuren",
@@ -3989,7 +4457,22 @@ var nl = {
3989
4457
  shadow: "Schaduw"
3990
4458
  },
3991
4459
  "from-consultation": "Uit een raadpleging",
3992
- "from-consultation-hint": "Een andere assistent raadpleegde de agent voor deze persoon; het voorstel draait alleen met de rechten van die persoon.",
4460
+ "from-consultation-hint": "Een andere assistent raadpleegde de agent voor iemand, en de agent stelde dit voor.",
4461
+ consultation: {
4462
+ "asked-by": "Gevraagd door {{name}}",
4463
+ "the-asker": "degene die het vroeg",
4464
+ route: {
4465
+ "asker-decides": "Degene die het vroeg, beslist in de eigen chat; het loopt met diens eigen rechten.",
4466
+ "subject-agent-rights": "De goedkeurders van het onderwerp, behalve degene die het vroeg, beslissen; het loopt met de eigen rechten van de agent.",
4467
+ "subject-asker-rights": "De goedkeurders van het onderwerp beslissen; het loopt met de rechten van degene die het vroeg."
4468
+ },
4469
+ "waiting-for-asker": "Wacht tot {{name}} beslist in de eigen chat.",
4470
+ "four-eyes": "Jij vroeg hierom, dus een andere goedkeurder beslist; het loopt met de eigen rechten van de agent.",
4471
+ "runs-with": {
4472
+ agent: "Loopt met de rechten van de agent",
4473
+ accountable: "Loopt met de rechten van het technische account"
4474
+ }
4475
+ },
3993
4476
  "give-feedback": "Feedback geven",
3994
4477
  "change-feedback": "Feedback wijzigen",
3995
4478
  "your-feedback": "Jouw feedback: {{category}}",
@@ -4068,6 +4551,11 @@ var nl = {
4068
4551
  running: "Bezig",
4069
4552
  completed: "Beantwoord",
4070
4553
  failed: "Niet beantwoord"
4554
+ },
4555
+ routes: {
4556
+ "asker-decides": "Vrager beslist: {{count}}",
4557
+ "subject-agent-rights": "Goedkeurders, rechten van de agent: {{count}}",
4558
+ "subject-asker-rights": "Goedkeurders, rechten van de vrager: {{count}}"
4071
4559
  }
4072
4560
  },
4073
4561
  "object-agents": {
@@ -4963,6 +5451,15 @@ class NhAssistantAdminApiService {
4963
5451
  setServiceAccount(agentId, request) {
4964
5452
  return this.request('POST', `admin/agents/${encodeURIComponent(agentId)}/service-account`, request);
4965
5453
  }
5454
+ /**
5455
+ * Switches the routes of the agent's consultations on, off or back to the setting in code; a route
5456
+ * left out keeps its switch. Code is the ceiling: switching on a route that no consultation in code
5457
+ * allows fails with `assistant-consult-route-not-allowed`. Returns the agent's state with its
5458
+ * `consultationRoutes`.
5459
+ */
5460
+ setConsultationRoutes(agentId, request) {
5461
+ return this.request('POST', `admin/agents/${encodeURIComponent(agentId)}/consultation-routes`, request);
5462
+ }
4966
5463
  /**
4967
5464
  * Puts an agent, in code or created here, in exactly `groups` of the run catalog's `agentGroups`
4968
5465
  * and out of the others. The choice wins over the agent's `InGroup` in code. Fails with a field error
@@ -5186,6 +5683,27 @@ class NhAssistantAdminApiService {
5186
5683
  setDataTriggerShadow(agentId, triggerId, request) {
5187
5684
  return this.request('POST', `${triggerPath(agentId, triggerId)}/shadow`, request);
5188
5685
  }
5686
+ /**
5687
+ * Adds or changes the consultation defined as data of an agent about one subject type, within the
5688
+ * agent's `consultationCeiling`. It is in effect once saved and answers when an asking agent selects
5689
+ * its consult tool. An agent that may get none fails with `assistant-consultations-not-allowed`, an
5690
+ * agent created here without run settings with `assistant-agent-not-configured`, a type the agent
5691
+ * allows consultation about in code with `assistant-consultation-in-code`, more than the ceiling's
5692
+ * `maxConsultations` with `assistant-configuration-limit`, a stale `expectedVersion` with
5693
+ * `assistant-version-conflict` and invalid input with field errors, such as `routes: not-allowed` for
5694
+ * a route outside the ceiling.
5695
+ */
5696
+ saveDataConsultation(agentId, subjectType, request) {
5697
+ return this.request('PUT', consultationPath(agentId, subjectType), request);
5698
+ }
5699
+ /**
5700
+ * Removes a consultation defined as data: its tools disappear, while its recorded proposals can still
5701
+ * be decided. Fails with `assistant-consultation-not-found` without one and with
5702
+ * `assistant-consultation-in-code` for a consultation in code.
5703
+ */
5704
+ removeDataConsultation(agentId, subjectType, removal = {}) {
5705
+ return this.request('DELETE', withQuery$1(consultationPath(agentId, subjectType), removal));
5706
+ }
5189
5707
  download(path) {
5190
5708
  return this.transport.download(path, 'application/x-ndjson', adminStatusCodes);
5191
5709
  }
@@ -5213,6 +5731,9 @@ function skillPath(agentId, skillId) {
5213
5731
  function triggerPath(agentId, triggerId) {
5214
5732
  return `${agentPath(agentId)}/triggers/${encodeURIComponent(triggerId)}`;
5215
5733
  }
5734
+ function consultationPath(agentId, subjectType) {
5735
+ return `${agentPath(agentId)}/consultations/${encodeURIComponent(subjectType)}`;
5736
+ }
5216
5737
  /** Appends the filter values that are set as query parameters. */
5217
5738
  function withQuery$1(path, filter) {
5218
5739
  const query = new URLSearchParams();
@@ -5399,7 +5920,10 @@ class NhAssistantAgentRunApiService {
5399
5920
  retryRun(runId, mode) {
5400
5921
  return this.transport.json('POST', `runs/${encodeURIComponent(runId)}/retry`, { mode });
5401
5922
  }
5402
- /** Pending proposals the caller may decide, sorted by expiry. */
5923
+ /**
5924
+ * Pending proposals the caller may decide, sorted by expiry, or with `grouped` by parent and subject,
5925
+ * with what is new since the caller's last visit (`visitInbox`).
5926
+ */
5403
5927
  getInbox(filter = {}) {
5404
5928
  const query = new URLSearchParams();
5405
5929
  for (const [key, value] of Object.entries(filter)) {
@@ -5410,6 +5934,15 @@ class NhAssistantAgentRunApiService {
5410
5934
  const suffix = query.toString();
5411
5935
  return this.transport.json('GET', suffix ? `approvals/inbox?${suffix}` : 'approvals/inbox');
5412
5936
  }
5937
+ /**
5938
+ * Records that the caller looked at the inbox now, so it marks the proposals made after this visit
5939
+ * as new. `divisionId` records the visit of the inbox of one division the caller may work in;
5940
+ * otherwise of the whole inbox. Fails with `assistant-subject-not-found` for a division the caller
5941
+ * may not work in.
5942
+ */
5943
+ visitInbox(divisionId) {
5944
+ return this.transport.json('POST', withQuery('approvals/inbox/visit', { divisionId }));
5945
+ }
5413
5946
  /**
5414
5947
  * Decides one proposal, bound to the proposal hash the person saw; the server checks the approval
5415
5948
  * policy and the tool's approve policy again. Accepts an inbox proposal or the approval part of an
@@ -6246,31 +6779,111 @@ function sameKey(current, expected) {
6246
6779
  /** The longest file name a file reference takes. */
6247
6780
  const NH_ASSISTANT_MAX_FILE_NAME_LENGTH = 256;
6248
6781
  const unknownMediaType = 'application/octet-stream';
6249
- /** The media types the platform names, with the extensions a file picker offers and a short label. */
6782
+ const zipAliases = ['application/zip', 'application/x-zip-compressed'];
6783
+ /**
6784
+ * The media types the platform names, with the extensions a file picker offers and a short label.
6785
+ * The extensions are the same as the server's table of upload media types.
6786
+ */
6250
6787
  const knownTypes = {
6251
6788
  'application/pdf': { extensions: ['.pdf'], label: 'PDF' },
6252
6789
  'image/png': { extensions: ['.png'], label: 'PNG' },
6253
6790
  'image/jpeg': { extensions: ['.jpg', '.jpeg'], label: 'JPEG' },
6791
+ 'image/gif': { extensions: ['.gif'], label: 'GIF' },
6792
+ 'image/webp': { extensions: ['.webp'], label: 'WebP' },
6254
6793
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document': { extensions: ['.docx'], label: 'Word' },
6255
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': { extensions: ['.xlsx'], label: 'Excel' }
6794
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': { extensions: ['.xlsx'], label: 'Excel' },
6795
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation': { extensions: ['.pptx'], label: 'PowerPoint' },
6796
+ 'application/msword': { extensions: ['.doc'], label: 'Word' },
6797
+ 'application/vnd.ms-excel': { extensions: ['.xls'], label: 'Excel' },
6798
+ 'text/csv': { extensions: ['.csv'], label: 'CSV' },
6799
+ 'text/tab-separated-values': { extensions: ['.tsv'], label: 'TSV' },
6800
+ 'text/plain': { extensions: ['.txt'], label: 'Text' },
6801
+ 'text/markdown': { extensions: ['.md', '.markdown'], label: 'Markdown' },
6802
+ 'application/json': { extensions: ['.json'], label: 'JSON' },
6803
+ 'application/xml': { extensions: ['.xml'], label: 'XML' }
6256
6804
  };
6805
+ /** Types that say nothing about the content, whatever the file's extension. */
6806
+ const genericTypes = new Set([unknownMediaType, 'binary/octet-stream', 'application/unknown']);
6257
6807
  /**
6258
- * The media type of a chosen file, lowercase and without parameters. A browser that does not know
6259
- * the type, as happens for Office documents, gives none; the extension of a known type then names
6260
- * it. Otherwise `application/octet-stream`.
6808
+ * The types browsers commonly give a file of an extension instead of its own type: the program
6809
+ * that opens it, as Windows does with `application/vnd.ms-excel` for `.csv`, an outdated name or
6810
+ * the container format.
6261
6811
  */
6262
- function nhAssistantMediaTypeOf(file) {
6263
- const declared = file.type.split(';')[0].trim().toLowerCase();
6264
- if (declared.length > 0 && declared !== unknownMediaType) {
6265
- return declared;
6266
- }
6267
- const name = file.name.toLowerCase();
6812
+ const aliasesByExtension = {
6813
+ '.csv': [
6814
+ 'application/vnd.ms-excel',
6815
+ 'application/csv',
6816
+ 'application/x-csv',
6817
+ 'text/x-csv',
6818
+ 'text/comma-separated-values',
6819
+ 'text/x-comma-separated-values'
6820
+ ],
6821
+ '.md': ['text/x-markdown'],
6822
+ '.markdown': ['text/x-markdown'],
6823
+ '.jpg': ['image/jpg', 'image/pjpeg'],
6824
+ '.jpeg': ['image/jpg', 'image/pjpeg'],
6825
+ '.png': ['image/x-png'],
6826
+ '.docx': zipAliases,
6827
+ '.xlsx': zipAliases,
6828
+ '.pptx': zipAliases,
6829
+ // Browsers send the older name of XML; an application that accepts only it keeps receiving it.
6830
+ '.xml': ['text/xml']
6831
+ };
6832
+ /** The extension of a file name in lowercase, such as `.csv`; empty without one. */
6833
+ function extensionOf(name) {
6834
+ const base = name.slice(Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')) + 1);
6835
+ const dot = base.lastIndexOf('.');
6836
+ return dot > 0 || (dot === 0 && base.length > 1) ? base.slice(dot).toLowerCase() : '';
6837
+ }
6838
+ /** The application's extensions (`AgentUploads.mappedExtensions`) whose type is accepted, lowercase. */
6839
+ function acceptedMappings(acceptedMediaTypes, mappedExtensions) {
6840
+ return (mappedExtensions ?? [])
6841
+ .map(mapping => ({ extension: mapping.extension.toLowerCase(), mediaType: mapping.mediaType.toLowerCase() }))
6842
+ .filter(mapping => acceptedMediaTypes.includes(mapping.mediaType));
6843
+ }
6844
+ /** The platform's media type of an extension, or `null`. */
6845
+ function knownMediaTypeOf(extension) {
6268
6846
  for (const [mediaType, known] of Object.entries(knownTypes)) {
6269
- if (known.extensions.some(extension => name.endsWith(extension))) {
6847
+ if (known.extensions.includes(extension)) {
6270
6848
  return mediaType;
6271
6849
  }
6272
6850
  }
6273
- return unknownMediaType;
6851
+ return null;
6852
+ }
6853
+ /**
6854
+ * The accepted type an extension names: the application's mapping, otherwise the platform's table;
6855
+ * `null` when neither names an accepted type.
6856
+ */
6857
+ function mediaTypeOfExtension(extension, acceptedMediaTypes, mappings) {
6858
+ if (extension.length === 0) {
6859
+ return null;
6860
+ }
6861
+ const mapped = mappings.find(mapping => mapping.extension === extension);
6862
+ if (mapped) {
6863
+ return mapped.mediaType;
6864
+ }
6865
+ const known = knownMediaTypeOf(extension);
6866
+ return known && acceptedMediaTypes.includes(known) ? known : null;
6867
+ }
6868
+ /**
6869
+ * The media type a chosen file uploads as, lowercase and without parameters, by the same rule as the
6870
+ * server. A browser gives no type, or a generic one, for a file its system does not know, and some
6871
+ * systems name an extension after the program that opens it, such as `application/vnd.ms-excel` for
6872
+ * a `.csv` file; the file name's extension then names the type, but only a type of
6873
+ * `acceptedMediaTypes`: the type the application mapped the extension to (`mappedExtensions`),
6874
+ * otherwise the platform's. A specific type stays as it is, so a type that is not accepted is refused
6875
+ * whatever the extension. Without a type and an accepted extension, `application/octet-stream`.
6876
+ */
6877
+ function nhAssistantMediaTypeOf(file, acceptedMediaTypes, mappedExtensions) {
6878
+ const declared = file.type.split(';')[0].trim().toLowerCase();
6879
+ const extension = extensionOf(file.name);
6880
+ if (declared.length === 0 || genericTypes.has(declared) || (aliasesByExtension[extension] ?? []).includes(declared)) {
6881
+ const derived = mediaTypeOfExtension(extension, acceptedMediaTypes, acceptedMappings(acceptedMediaTypes, mappedExtensions));
6882
+ if (derived) {
6883
+ return derived;
6884
+ }
6885
+ }
6886
+ return declared.length > 0 ? declared : unknownMediaType;
6274
6887
  }
6275
6888
  /**
6276
6889
  * The failure code of a file before it is uploaded, or `null` when it may be: the most files of one
@@ -6292,11 +6905,19 @@ function nhAssistantFileProblem(file, mediaType, uploads, attached) {
6292
6905
  }
6293
6906
  return null;
6294
6907
  }
6295
- /** The `accept` attribute of a file input for these media types: each type and its known extensions. */
6296
- function nhAssistantAcceptAttribute(mediaTypes) {
6297
- return mediaTypes
6298
- .flatMap(mediaType => [mediaType, ...(knownTypes[mediaType]?.extensions ?? [])])
6299
- .join(',');
6908
+ /**
6909
+ * The `accept` attribute of a file input for these accepted media types: each type and the extensions
6910
+ * that name it, those the application mapped to it (`mappedExtensions`) and those of the platform's
6911
+ * table that the application did not map to another accepted type.
6912
+ */
6913
+ function nhAssistantAcceptAttribute(mediaTypes, mappedExtensions) {
6914
+ const mappings = acceptedMappings(mediaTypes, mappedExtensions);
6915
+ const values = mediaTypes.flatMap(mediaType => [
6916
+ mediaType,
6917
+ ...mappings.filter(mapping => mapping.mediaType === mediaType).map(mapping => mapping.extension),
6918
+ ...(knownTypes[mediaType]?.extensions ?? []).filter(extension => !mappings.some(mapping => mapping.extension === extension))
6919
+ ]);
6920
+ return [...new Set(values)].join(',');
6300
6921
  }
6301
6922
  /** A short name of a media type, such as `PDF` or `Word`; otherwise its subtype in capitals. */
6302
6923
  function nhAssistantFileTypeLabel(mediaType) {
@@ -6421,7 +7042,11 @@ function applyNhAssistantEvent(conversation, event, clientMessageId) {
6421
7042
  };
6422
7043
  }
6423
7044
  }
6424
- /** Records the user's approval decision locally until the server state is reloaded. */
7045
+ /**
7046
+ * Records the user's approval decision locally until the server state is reloaded. A consulted
7047
+ * agent's proposal that the user left for the subject's approvers (`leave`) closes here as the server
7048
+ * closes it, rejected in this conversation, and can no longer be left; the waiting tool call goes on.
7049
+ */
6425
7050
  function applyNhAssistantApprovalDecision(conversation, approvalId, decision) {
6426
7051
  const approval = conversation.messages
6427
7052
  .flatMap(message => message.parts)
@@ -6431,7 +7056,7 @@ function applyNhAssistantApprovalDecision(conversation, approvalId, decision) {
6431
7056
  const messages = conversation.messages.map(message => ({
6432
7057
  ...message,
6433
7058
  parts: message.parts.map(part => part.type === 'approval' && part.approvalId === approvalId
6434
- ? { ...part, status: decision === 'approve' ? 'approved' : 'rejected' }
7059
+ ? decidedApproval(part, decision)
6435
7060
  : part)
6436
7061
  }));
6437
7062
  let updated = {
@@ -6445,15 +7070,24 @@ function applyNhAssistantApprovalDecision(conversation, approvalId, decision) {
6445
7070
  if (awaiting) {
6446
7071
  updated = updateToolCall(updated, awaiting.invocationId, part => ({
6447
7072
  ...part,
6448
- status: decision === 'approve' ? 'running' : 'rejected'
7073
+ status: decision === 'reject' ? 'rejected' : 'running'
6449
7074
  }));
6450
7075
  }
6451
7076
  }
6452
7077
  return updated;
6453
7078
  }
7079
+ /** An approval part after the user's decision. */
7080
+ function decidedApproval(part, decision) {
7081
+ const decided = { ...part, status: decision === 'approve' ? 'approved' : 'rejected' };
7082
+ if (decision === 'leave' && part.consultation) {
7083
+ decided.consultation = { ...part.consultation, leaveRoute: null };
7084
+ }
7085
+ return decided;
7086
+ }
6454
7087
  /**
6455
- * Applies a live update of another session: `message.created` adds a participant's message,
6456
- * the turn events behave exactly like the starting request's stream. Unknown types are ignored.
7088
+ * Applies a live update of another session: `message.created` adds a participant's or the
7089
+ * application's message once, where its sequence number puts it; the turn events behave exactly
7090
+ * like the starting request's stream. Unknown types are ignored.
6457
7091
  */
6458
7092
  function applyNhAssistantLiveEvent(conversation, event) {
6459
7093
  if (event.type === 'message.created') {
@@ -6461,7 +7095,7 @@ function applyNhAssistantLiveEvent(conversation, event) {
6461
7095
  if (!message?.id || conversation.messages.some(existing => existing.id === message.id)) {
6462
7096
  return conversation;
6463
7097
  }
6464
- return { ...conversation, messages: [...conversation.messages, message] };
7098
+ return { ...conversation, messages: insertBySequence(conversation.messages, message) };
6465
7099
  }
6466
7100
  if (!NH_ASSISTANT_SSE_EVENT_TYPES.includes(event.type)) {
6467
7101
  return conversation;
@@ -6508,6 +7142,26 @@ function conversationStatusFor(status) {
6508
7142
  return 'failed';
6509
7143
  }
6510
7144
  }
7145
+ /**
7146
+ * Places a stored message before the messages at the end that have a higher sequence number. A
7147
+ * message without a number, such as an optimistic message or the answer a running turn is still
7148
+ * writing, counts as earlier: the server numbers a turn's messages when the turn starts, so a message
7149
+ * stored during the turn follows its answer. A message without a number of its own goes last.
7150
+ */
7151
+ function insertBySequence(messages, message) {
7152
+ const sequence = message.sequence;
7153
+ let index = messages.length;
7154
+ if (typeof sequence === 'number' && sequence > 0) {
7155
+ while (index > 0) {
7156
+ const previous = messages[index - 1].sequence;
7157
+ if (typeof previous !== 'number' || previous <= sequence) {
7158
+ break;
7159
+ }
7160
+ index--;
7161
+ }
7162
+ }
7163
+ return [...messages.slice(0, index), message, ...messages.slice(index)];
7164
+ }
6511
7165
  function createAssistantMessage(id) {
6512
7166
  return { id, role: 'assistant', createdAt: new Date().toISOString(), parts: [] };
6513
7167
  }
@@ -6763,6 +7417,8 @@ const NH_ASSISTANT_NO_ANSWER_NOTICE_CODE = 'assistant-no-answer';
6763
7417
  */
6764
7418
  const NH_ASSISTANT_MESSAGE_QUEUED_NOTICE_CODE = 'assistant-message-queued';
6765
7419
  const fourEyesRequiredCode = 'assistant-four-eyes-required';
7420
+ /** A consultation's proposal in the consulted thread that the person who asked decides in their own chat. */
7421
+ const askerDecidesCode = 'assistant-consult-asker-decides';
6766
7422
  /** True when a conversation has messages the caller has not read. A running turn is not unread yet. */
6767
7423
  function nhAssistantIsUnread(summary) {
6768
7424
  if (summary.status === 'running' || summary.lastReadSequence === undefined) {
@@ -6876,6 +7532,12 @@ class NhAssistantStore {
6876
7532
  /** Non-blocking remark about the latest completed turn; shown only while no error is shown. */
6877
7533
  this.notice = computed(() => this.activeSession()?.notice ?? null, ...(ngDevMode ? [{ debugName: "notice" }] : []));
6878
7534
  this.lastUsage = computed(() => this.activeSession()?.lastUsage ?? null, ...(ngDevMode ? [{ debugName: "lastUsage" }] : []));
7535
+ /**
7536
+ * The answer the running turn of the open conversation writes, while this tab streams the turn or
7537
+ * follows it live; `null` otherwise. Pass it to `nh-assistant-thread` as `writingMessageId`, so
7538
+ * the working indicator stays under that answer when a message arrives after it during the turn.
7539
+ */
7540
+ this.writingMessageId = computed(() => this.activeSession()?.writingMessageId ?? null, ...(ngDevMode ? [{ debugName: "writingMessageId" }] : []));
6879
7541
  /** Text of a message the server did not accept, so the composer can offer it again. */
6880
7542
  this.restoredDraft = this.restoredDraftState.asReadonly();
6881
7543
  /** Page context that the next message would send, for the transparency chip. */
@@ -6960,7 +7622,7 @@ class NhAssistantStore {
6960
7622
  const decision = decisions.get(approval.approvalId);
6961
7623
  rights.set(approval.approvalId, {
6962
7624
  canDecide: decision?.canDecide === true,
6963
- waitingFor: decision?.reasonCode === fourEyesRequiredCode ? 'other-approver' : 'approver',
7625
+ waitingFor: waitingForOf(decision?.reasonCode),
6964
7626
  decisionOwner: null
6965
7627
  });
6966
7628
  }
@@ -7463,8 +8125,11 @@ class NhAssistantStore {
7463
8125
  /**
7464
8126
  * Approves or rejects an approval of the open conversation: `approvalId`, or the pending approval
7465
8127
  * when it is left out. A proposal of an agent's run is decided with its proposal hash through the
7466
- * agent-run endpoint; other approvals resume their turn as a stream. Ignores calls the current
7467
- * user may not make and repeated calls while a decision is in flight.
8128
+ * agent-run endpoint; other approvals resume their turn as a stream. `leave` leaves a consulted
8129
+ * agent's proposal that has a `consultation.leaveRoute` for the subject's approvers; it resumes the
8130
+ * turn like a decision and reads the conversation again, which then names the proposal that waits
8131
+ * at the subject. Ignores calls the current user may not make, `leave` for other approvals and
8132
+ * repeated calls while a decision is in flight.
7468
8133
  */
7469
8134
  decide(decision, reason, approvalId) {
7470
8135
  const session = this.activeSession();
@@ -7478,9 +8143,15 @@ class NhAssistantStore {
7478
8143
  if (!approval || this.approvalRights().get(approval.approvalId)?.canDecide !== true) {
7479
8144
  return;
7480
8145
  }
8146
+ if (decision === 'leave' && !approval.consultation?.leaveRoute) {
8147
+ // Only a consulted agent's proposal with a route at the subject can be left for its approvers.
8148
+ return;
8149
+ }
7481
8150
  const proposal = conversation.thread?.decisions.find(item => item.approvalId === approval.approvalId);
7482
8151
  if (proposal?.runId) {
7483
- void this.decideProposal(conversation.id, approval, decision, reason);
8152
+ if (decision !== 'leave') {
8153
+ void this.decideProposal(conversation.id, approval, decision, reason);
8154
+ }
7484
8155
  return;
7485
8156
  }
7486
8157
  if (conversation.pendingApproval?.approvalId !== approval.approvalId) {
@@ -7512,7 +8183,8 @@ class NhAssistantStore {
7512
8183
  },
7513
8184
  complete: () => {
7514
8185
  this.patchSession(conversationId, current => ({ ...current, deciding: false }));
7515
- this.afterTurn(conversationId, failed);
8186
+ // A proposal left for the subject's approvers names the one that waits there only once read again.
8187
+ this.afterTurn(conversationId, failed || decision === 'leave');
7516
8188
  }
7517
8189
  });
7518
8190
  }
@@ -7586,7 +8258,7 @@ class NhAssistantStore {
7586
8258
  if (this.sessionsState().get(conversationId)?.streaming) {
7587
8259
  this.streams.get(conversationId)?.unsubscribe();
7588
8260
  this.streams.delete(conversationId);
7589
- this.patchSession(conversationId, current => ({ ...current, streaming: false, deciding: false }));
8261
+ this.patchSession(conversationId, current => ({ ...current, streaming: false, deciding: false, writingMessageId: null }));
7590
8262
  void this.reloadSession(conversationId);
7591
8263
  }
7592
8264
  }, cancelGracePeriodMs));
@@ -7759,7 +8431,7 @@ class NhAssistantStore {
7759
8431
  const added = [];
7760
8432
  const pending = [];
7761
8433
  for (const file of files) {
7762
- const mediaType = nhAssistantMediaTypeOf(file);
8434
+ const mediaType = nhAssistantMediaTypeOf(file, uploads.acceptedMediaTypes, uploads.mappedExtensions);
7763
8435
  const problem = nhAssistantFileProblem(file, mediaType, uploads, attached);
7764
8436
  const attachment = {
7765
8437
  id: createClientMessageId(),
@@ -8070,12 +8742,12 @@ class NhAssistantStore {
8070
8742
  }
8071
8743
  runStream(conversationId, stream, handlers) {
8072
8744
  this.streams.get(conversationId)?.unsubscribe();
8073
- this.patchSession(conversationId, session => ({ ...session, streaming: true }));
8745
+ this.patchSession(conversationId, session => ({ ...session, streaming: true, writingMessageId: null }));
8074
8746
  const finish = () => {
8075
8747
  clearTimeout(this.cancelTimers.get(conversationId));
8076
8748
  this.cancelTimers.delete(conversationId);
8077
8749
  this.streams.delete(conversationId);
8078
- this.patchSession(conversationId, session => ({ ...session, streaming: false }));
8750
+ this.patchSession(conversationId, session => ({ ...session, streaming: false, writingMessageId: null }));
8079
8751
  handlers.complete();
8080
8752
  };
8081
8753
  const subscription = stream.subscribe({
@@ -8120,7 +8792,7 @@ class NhAssistantStore {
8120
8792
  notice = completionNotice(conversation, event.data.errorCode);
8121
8793
  }
8122
8794
  }
8123
- return { ...session, conversation, error, notice, lastUsage };
8795
+ return { ...session, conversation, error, notice, lastUsage, writingMessageId: writingAnswerAfter(session.writingMessageId, event) };
8124
8796
  });
8125
8797
  }
8126
8798
  afterTurn(conversationId, reload) {
@@ -8208,7 +8880,8 @@ class NhAssistantStore {
8208
8880
  const wasRunning = session.conversation.status === 'running';
8209
8881
  this.patchSession(change.conversationId, current => ({
8210
8882
  ...current,
8211
- conversation: applyNhAssistantConversationChange(current.conversation, change)
8883
+ conversation: applyNhAssistantConversationChange(current.conversation, change),
8884
+ writingMessageId: change.status === 'running' ? current.writingMessageId : null
8212
8885
  }));
8213
8886
  if (wasRunning && change.status !== 'running') {
8214
8887
  // Another session ended its turn; the snapshot holds the exact answer and approvals.
@@ -8231,18 +8904,27 @@ class NhAssistantStore {
8231
8904
  this.draftErrorState.set(clientError(NhAssistantClientErrorCodes.conversationRemoved));
8232
8905
  }
8233
8906
  }
8907
+ /**
8908
+ * Applies a live turn event or message to a conversation this tab shows. While the tab streams a
8909
+ * turn of the conversation, its stream brings the turn's events and its own message, so it applies
8910
+ * only messages that others stored meanwhile: the application's and other people's.
8911
+ */
8234
8912
  onLiveEvent(event) {
8235
8913
  const session = this.sessionsState().get(event.conversationId);
8236
8914
  if (event.type === 'turn.started') {
8237
8915
  this.patchSummary(event.conversationId, summary => ({ ...summary, status: 'running', activeActorId: event.actorId }));
8238
8916
  }
8239
- if (!session || session.streaming) {
8240
- // This tab streams the turn itself, or does not show the conversation.
8917
+ if (!session) {
8918
+ return;
8919
+ }
8920
+ if (session.streaming && !isMessageOfOthers(session.conversation, event)) {
8241
8921
  return;
8242
8922
  }
8243
8923
  this.patchSession(event.conversationId, current => ({
8244
8924
  ...current,
8245
- conversation: applyNhAssistantLiveEvent(current.conversation, event)
8925
+ conversation: applyNhAssistantLiveEvent(current.conversation, event),
8926
+ // A turn this tab follows live names its answer; while the tab streams, its stream does.
8927
+ writingMessageId: current.streaming ? current.writingMessageId : writingAnswerAfter(current.writingMessageId, event)
8246
8928
  }));
8247
8929
  }
8248
8930
  /**
@@ -8294,8 +8976,8 @@ class NhAssistantStore {
8294
8976
  const next = new Map(sessions);
8295
8977
  const existing = next.get(conversation.id);
8296
8978
  next.set(conversation.id, existing
8297
- ? { ...existing, conversation }
8298
- : { conversation, streaming: false, deciding: false, error: null, notice: null, lastUsage: null });
8979
+ ? { ...existing, conversation, writingMessageId: conversation.status === 'running' ? existing.writingMessageId : null }
8980
+ : { conversation, streaming: false, deciding: false, error: null, notice: null, lastUsage: null, writingMessageId: null });
8299
8981
  if (next.size > maxSessions) {
8300
8982
  const activeId = this.activeIdState();
8301
8983
  for (const [id, session] of next) {
@@ -8424,16 +9106,59 @@ function messageQueuedNotice(fileCount) {
8424
9106
  messageKey: fileCount > 0 ? 'nh-assistant.notices.message-queued-with-files' : 'nh-assistant.notices.message-queued'
8425
9107
  };
8426
9108
  }
8427
- /** A completed turn with a server code, or without any answer text, explains itself with a notice. */
9109
+ /**
9110
+ * The answer a turn writes after `event`: `turn.started` names it, `turn.completed` and `error` end
9111
+ * the turn, and every other event leaves it as it is.
9112
+ */
9113
+ function writingAnswerAfter(current, event) {
9114
+ switch (event.type) {
9115
+ case 'turn.started':
9116
+ return event.data?.assistantMessageId ?? null;
9117
+ case 'turn.completed':
9118
+ case 'error':
9119
+ return null;
9120
+ default:
9121
+ return current;
9122
+ }
9123
+ }
9124
+ /**
9125
+ * A completed turn with a server code, or without any answer text, explains itself with a notice.
9126
+ * Another person's message that arrived while the turn ran does not end this tab's turn.
9127
+ */
8428
9128
  function completionNotice(conversation, errorCode) {
8429
9129
  if (errorCode) {
8430
9130
  return clientError(errorCode);
8431
9131
  }
8432
- if (!nhAssistantLatestTurnHasText(conversation)) {
9132
+ const ownTurn = { ...conversation, messages: conversation.messages.filter(message => !isOtherPersonsMessage(conversation, message)) };
9133
+ if (!nhAssistantLatestTurnHasText(ownTurn)) {
8433
9134
  return { code: NH_ASSISTANT_NO_ANSWER_NOTICE_CODE, messageKey: 'nh-assistant.notices.no-answer' };
8434
9135
  }
8435
9136
  return null;
8436
9137
  }
9138
+ /**
9139
+ * True for a live `message.created` of a message that this tab did not write: the application's, or
9140
+ * a user message of another writer. A user message is this tab's own when the current user wrote it,
9141
+ * and when the tab does not know the current user, so a turn's own message never shows twice.
9142
+ */
9143
+ function isMessageOfOthers(conversation, event) {
9144
+ if (event.type !== 'message.created') {
9145
+ return false;
9146
+ }
9147
+ const message = event.data;
9148
+ if (!message?.id) {
9149
+ return false;
9150
+ }
9151
+ if (message.role === 'application') {
9152
+ return true;
9153
+ }
9154
+ return message.role === 'user' && isOtherPersonsMessage(conversation, message, event.actorId);
9155
+ }
9156
+ /** True for a user message that a known writer other than the current user wrote. */
9157
+ function isOtherPersonsMessage(conversation, message, fallbackAuthor) {
9158
+ const author = message.authorActorId ?? fallbackAuthor;
9159
+ const currentActorId = conversation.currentActorId;
9160
+ return message.role === 'user' && !!author && !!currentActorId && author !== currentActorId;
9161
+ }
8437
9162
  /** The approvals of a conversation that still wait for a decision, each once. */
8438
9163
  /** Every approval of the conversation once, by approval id; later parts win. */
8439
9164
  function approvalParts(conversation) {
@@ -8461,6 +9186,20 @@ function pendingApprovalParts(conversation) {
8461
9186
  }
8462
9187
  return [...approvals.values()];
8463
9188
  }
9189
+ /**
9190
+ * Who decides an open proposal of an agent thread the caller may not decide, by the server's reason:
9191
+ * the person who consulted the agent, another approver for four-eyes, otherwise an approver.
9192
+ */
9193
+ function waitingForOf(reasonCode) {
9194
+ switch (reasonCode) {
9195
+ case askerDecidesCode:
9196
+ return 'asker';
9197
+ case fourEyesRequiredCode:
9198
+ return 'other-approver';
9199
+ default:
9200
+ return 'approver';
9201
+ }
9202
+ }
8464
9203
  /**
8465
9204
  * Shows the decision of an agent's proposal until the reloaded snapshot arrives: the approval's new
8466
9205
  * status, no open decision for it and, for the approval the conversation waited for, no pending one.
@@ -9132,6 +9871,7 @@ const NH_ASSISTANT_SERVER_ERROR_CODES = [
9132
9871
  'assistant-service-account-not-found',
9133
9872
  'assistant-skill-in-code',
9134
9873
  'assistant-trigger-in-code',
9874
+ 'assistant-trigger-filter-not-found',
9135
9875
  'assistant-skill-in-use',
9136
9876
  'assistant-configuration-limit',
9137
9877
  'assistant-run-settings-in-code',
@@ -9193,6 +9933,14 @@ const NH_ASSISTANT_SERVER_ERROR_CODES = [
9193
9933
  'assistant-consult-timeout',
9194
9934
  'assistant-consult-failed',
9195
9935
  'assistant-consult-read-only',
9936
+ 'assistant-consult-proposal-refused',
9937
+ 'assistant-consult-asker-decides',
9938
+ 'assistant-consult-route-not-allowed',
9939
+ 'assistant-consult-proposal-moved',
9940
+ // Consultations defined as data in the administration
9941
+ 'assistant-consultation-in-code',
9942
+ 'assistant-consultation-not-found',
9943
+ 'assistant-consultations-not-allowed',
9196
9944
  // Preferences and administration
9197
9945
  'assistant-validation',
9198
9946
  'assistant-instructions-too-long',
@@ -9669,8 +10417,11 @@ class NhAssistantComposerComponent {
9669
10417
  /** True while a file of the next message still uploads. */
9670
10418
  this.uploading = computed(() => this.attachments().some(attachment => attachment.state === 'uploading'), ...(ngDevMode ? [{ debugName: "uploading" }] : []));
9671
10419
  this.canSend = computed(() => !this.disabled() && !this.sendDisabled() && this.length() > 0 && !this.tooLong() && !this.uploading(), ...(ngDevMode ? [{ debugName: "canSend" }] : []));
9672
- /** The `accept` attribute of the file input. */
9673
- this.accept = computed(() => nhAssistantAcceptAttribute(this.uploads()?.acceptedMediaTypes ?? []), ...(ngDevMode ? [{ debugName: "accept" }] : []));
10420
+ /** The `accept` attribute of the file input, with the extensions the application mapped. */
10421
+ this.accept = computed(() => {
10422
+ const uploads = this.uploads();
10423
+ return nhAssistantAcceptAttribute(uploads?.acceptedMediaTypes ?? [], uploads?.mappedExtensions);
10424
+ }, ...(ngDevMode ? [{ debugName: "accept" }] : []));
9674
10425
  /** The values the attachment texts name: the accepted types, the largest size and the most files. */
9675
10426
  this.uploadParams = computed(() => {
9676
10427
  const uploads = this.uploads();
@@ -10289,6 +11040,159 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
10289
11040
  args: [{ name: 'nhAssistantMarkdown', standalone: true }]
10290
11041
  }] });
10291
11042
 
11043
+ /**
11044
+ * The name of a subject with the name of its type, linked to where the application shows the
11045
+ * subject (internal). The host's `subjectLinks` builder wins over the server's link; a link within
11046
+ * the application navigates through the router, an `https` link leaves it, and anything else shows
11047
+ * no link. Ctrl-, Cmd-, Shift- and middle-clicks keep the browser's behavior, such as a new tab.
11048
+ * A name that already says the type, as "The whole application" does for the application as a
11049
+ * whole, is shown once.
11050
+ */
11051
+ class NhAssistantSubjectLinkComponent {
11052
+ constructor() {
11053
+ this.config = inject(NH_ASSISTANT_CONFIG, { optional: true });
11054
+ this.router = inject(Router, { optional: true });
11055
+ this.locationStrategy = inject(LocationStrategy, { optional: true });
11056
+ this.injector = inject(EnvironmentInjector);
11057
+ this.subjectType = input.required(...(ngDevMode ? [{ debugName: "subjectType" }] : []));
11058
+ this.subjectId = input.required(...(ngDevMode ? [{ debugName: "subjectId" }] : []));
11059
+ /** The text people read, usually the subject's display name. */
11060
+ this.label = input.required(...(ngDevMode ? [{ debugName: "label" }] : []));
11061
+ /** The name of the subject type, such as "Project"; nothing is shown without one. */
11062
+ this.typeName = input(null, ...(ngDevMode ? [{ debugName: "typeName" }] : []));
11063
+ /** The server's link to the subject. */
11064
+ this.link = input(null, ...(ngDevMode ? [{ debugName: "link" }] : []));
11065
+ /** The type's name, unless the subject's name is the type's name itself, as for the whole application. */
11066
+ this.shownTypeName = computed(() => {
11067
+ const type = this.typeName()?.trim();
11068
+ if (!type) {
11069
+ return null;
11070
+ }
11071
+ return type.toLocaleLowerCase() === this.label().trim().toLocaleLowerCase() ? null : type;
11072
+ }, ...(ngDevMode ? [{ debugName: "shownTypeName" }] : []));
11073
+ this.target = computed(() => {
11074
+ const builder = this.config?.subjectLinks?.[this.subjectType()];
11075
+ const subject = { type: this.subjectType(), id: this.subjectId() };
11076
+ const custom = builder ? runInInjectionContext(this.injector, () => builder(subject)) : undefined;
11077
+ if (custom === null) {
11078
+ return null;
11079
+ }
11080
+ if (Array.isArray(custom)) {
11081
+ return this.routeTarget(this.router ? this.router.serializeUrl(this.router.createUrlTree([...custom])) : null);
11082
+ }
11083
+ return this.urlTarget(typeof custom === 'string' ? custom : this.link() ?? null);
11084
+ }, ...(ngDevMode ? [{ debugName: "target" }] : []));
11085
+ }
11086
+ open(event, target) {
11087
+ nhAssistantOpenLink(event, target, this.router);
11088
+ }
11089
+ urlTarget(url) {
11090
+ return nhAssistantLinkTarget(url, this.router, this.locationStrategy);
11091
+ }
11092
+ routeTarget(url) {
11093
+ return nhAssistantRouteTarget(url, this.router, this.locationStrategy);
11094
+ }
11095
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantSubjectLinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
11096
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", type: NhAssistantSubjectLinkComponent, isStandalone: true, selector: "nh-assistant-subject-link", inputs: { subjectType: { classPropertyName: "subjectType", publicName: "subjectType", isSignal: true, isRequired: true, transformFunction: null }, subjectId: { classPropertyName: "subjectId", publicName: "subjectId", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, typeName: { classPropertyName: "typeName", publicName: "typeName", isSignal: true, isRequired: false, transformFunction: null }, link: { classPropertyName: "link", publicName: "link", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
11097
+ @if (shownTypeName(); as type) {
11098
+ <!-- The space keeps the type and the name apart for screen readers; the flex gap shows it. -->
11099
+ <span class="nh-assistant-subject-type">{{ type }}</span>{{ ' ' }}
11100
+ }
11101
+ @if (target(); as link) {
11102
+ <a class="nh-assistant-subject-name" [href]="link.href" (click)="open($event, link)">{{ label() }}</a>
11103
+ } @else {
11104
+ <span class="nh-assistant-subject-name">{{ label() }}</span>
11105
+ }
11106
+ `, isInline: true, styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:inline-flex;flex-wrap:wrap;align-items:baseline;gap:.35em;min-width:0;overflow-wrap:anywhere}.nh-assistant-subject-type{color:var(--nha-text-muted);font-size:.85em;font-weight:500}a.nh-assistant-subject-name{border-radius:2px;color:inherit;text-decoration:underline;text-underline-offset:.15em}a.nh-assistant-subject-name:hover{color:var(--nha-accent)}a.nh-assistant-subject-name:focus-visible{outline:2px solid var(--nha-focus);outline-offset:2px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11107
+ }
11108
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantSubjectLinkComponent, decorators: [{
11109
+ type: Component,
11110
+ args: [{ selector: 'nh-assistant-subject-link', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
11111
+ @if (shownTypeName(); as type) {
11112
+ <!-- The space keeps the type and the name apart for screen readers; the flex gap shows it. -->
11113
+ <span class="nh-assistant-subject-type">{{ type }}</span>{{ ' ' }}
11114
+ }
11115
+ @if (target(); as link) {
11116
+ <a class="nh-assistant-subject-name" [href]="link.href" (click)="open($event, link)">{{ label() }}</a>
11117
+ } @else {
11118
+ <span class="nh-assistant-subject-name">{{ label() }}</span>
11119
+ }
11120
+ `, styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:inline-flex;flex-wrap:wrap;align-items:baseline;gap:.35em;min-width:0;overflow-wrap:anywhere}.nh-assistant-subject-type{color:var(--nha-text-muted);font-size:.85em;font-weight:500}a.nh-assistant-subject-name{border-radius:2px;color:inherit;text-decoration:underline;text-underline-offset:.15em}a.nh-assistant-subject-name:hover{color:var(--nha-accent)}a.nh-assistant-subject-name:focus-visible{outline:2px solid var(--nha-focus);outline-offset:2px}\n"] }]
11121
+ }], propDecorators: { subjectType: [{ type: i0.Input, args: [{ isSignal: true, alias: "subjectType", required: true }] }], subjectId: [{ type: i0.Input, args: [{ isSignal: true, alias: "subjectId", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], typeName: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeName", required: false }] }], link: [{ type: i0.Input, args: [{ isSignal: true, alias: "link", required: false }] }] } });
11122
+ /**
11123
+ * Where a link the server sent leads (internal): a path within the application, which the router
11124
+ * opens, an `https` address elsewhere, or `null` for anything else, so no other scheme reaches an
11125
+ * anchor. Subject links and the links of application messages follow this rule.
11126
+ */
11127
+ function nhAssistantLinkTarget(url, router, locationStrategy) {
11128
+ if (!url) {
11129
+ return null;
11130
+ }
11131
+ if (url.startsWith('/') && !url.startsWith('//') && !url.startsWith('/\\')) {
11132
+ return nhAssistantRouteTarget(url, router, locationStrategy);
11133
+ }
11134
+ return /^https:\/\/[^/\\\s]+/i.test(url) ? { href: url, route: null } : null;
11135
+ }
11136
+ /** A route of the application as the browser shows it and as the router opens it (internal). */
11137
+ function nhAssistantRouteTarget(url, router, locationStrategy) {
11138
+ if (!url) {
11139
+ return null;
11140
+ }
11141
+ return {
11142
+ href: locationStrategy ? locationStrategy.prepareExternalUrl(url) : url,
11143
+ route: router ? url : null
11144
+ };
11145
+ }
11146
+ /**
11147
+ * Opens a route of the application through the router on a plain click (internal). Ctrl-, Cmd-,
11148
+ * Shift-, Alt- and middle-clicks, and addresses outside the application, keep the browser's behavior.
11149
+ */
11150
+ function nhAssistantOpenLink(event, target, router) {
11151
+ if (!target.route || !router || event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) {
11152
+ return;
11153
+ }
11154
+ event.preventDefault();
11155
+ void router.navigateByUrl(target.route);
11156
+ }
11157
+
11158
+ /**
11159
+ * The link of a message the application posted (internal): a path within the application opens
11160
+ * through the router, an `https` address leaves it, and anything else shows nothing, by the same rule
11161
+ * as subject links.
11162
+ */
11163
+ class NhAssistantMessageLinkComponent {
11164
+ constructor() {
11165
+ this.router = inject(Router, { optional: true });
11166
+ this.locationStrategy = inject(LocationStrategy, { optional: true });
11167
+ /** A path within the application or an `https` address. */
11168
+ this.url = input.required(...(ngDevMode ? [{ debugName: "url" }] : []));
11169
+ /** The text people read. */
11170
+ this.label = input.required(...(ngDevMode ? [{ debugName: "label" }] : []));
11171
+ this.target = computed(() => nhAssistantLinkTarget(this.url(), this.router, this.locationStrategy), ...(ngDevMode ? [{ debugName: "target" }] : []));
11172
+ }
11173
+ open(event, target) {
11174
+ nhAssistantOpenLink(event, target, this.router);
11175
+ }
11176
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantMessageLinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
11177
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", type: NhAssistantMessageLinkComponent, isStandalone: true, selector: "nh-assistant-message-link", inputs: { url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
11178
+ @if (target(); as link) {
11179
+ <a class="nh-assistant-message-link" [href]="link.href" (click)="open($event, link)">
11180
+ <nh-assistant-icon name="link" />{{ label() }}
11181
+ </a>
11182
+ }
11183
+ `, isInline: true, styles: [":host{display:block}.nh-assistant-message-link{display:inline-flex;align-items:center;gap:6px;color:var(--nha-accent);font-size:13px;font-weight:600;text-decoration:none}.nh-assistant-message-link:hover{text-decoration:underline}.nh-assistant-message-link:focus-visible{outline:2px solid var(--nha-focus);outline-offset:2px;border-radius:4px}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11184
+ }
11185
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantMessageLinkComponent, decorators: [{
11186
+ type: Component,
11187
+ args: [{ selector: 'nh-assistant-message-link', standalone: true, imports: [NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
11188
+ @if (target(); as link) {
11189
+ <a class="nh-assistant-message-link" [href]="link.href" (click)="open($event, link)">
11190
+ <nh-assistant-icon name="link" />{{ label() }}
11191
+ </a>
11192
+ }
11193
+ `, styles: [":host{display:block}.nh-assistant-message-link{display:inline-flex;align-items:center;gap:6px;color:var(--nha-accent);font-size:13px;font-weight:600;text-decoration:none}.nh-assistant-message-link:hover{text-decoration:underline}.nh-assistant-message-link:focus-visible{outline:2px solid var(--nha-focus);outline-offset:2px;border-radius:4px}\n"] }]
11194
+ }], propDecorators: { url: [{ type: i0.Input, args: [{ isSignal: true, alias: "url", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }] } });
11195
+
10292
11196
  /**
10293
11197
  * Groups consecutive tool calls of a message, and consecutive files. Text and approvals break a
10294
11198
  * group, so an approval always stays visible. A tool group's key is its first invocation id, so it
@@ -10473,10 +11377,16 @@ function formatNhAssistantCountdown(milliseconds) {
10473
11377
  * proposed arguments and a countdown to expiry. A decision is emitted at most once until
10474
11378
  * the host reports that it is no longer deciding. After the user rejected an agent's proposal
10475
11379
  * (`canGiveFeedback`), a low-key action asks what was wrong. A proposal that closed because it no
10476
- * longer fits shows why, such as "Already done", with the application's explanation.
11380
+ * longer fits shows why, such as "Already done", with the application's explanation. A proposal of
11381
+ * another agent the assistant consulted (`consultation`) names that agent and the subject, offers the
11382
+ * agent's answer and says that approving runs it with the user's own rights; with a
11383
+ * `consultation.leaveRoute` a third action leaves it for the subject's approvers (`leave`), after which
11384
+ * the card says that it waits for them.
10477
11385
  */
10478
11386
  class NhAssistantApprovalCardComponent {
10479
11387
  constructor() {
11388
+ /** Names the consulted agent as the status does; the card also works outside `provideNhAssistant`. */
11389
+ this.store = inject(NhAssistantStore, { optional: true });
10480
11390
  this.approval = input.required(...(ngDevMode ? [{ debugName: "approval" }] : []));
10481
11391
  /** True while the host submits a decision; disables both buttons. */
10482
11392
  this.deciding = input(false, ...(ngDevMode ? [{ debugName: "deciding" }] : []));
@@ -10501,6 +11411,8 @@ class NhAssistantApprovalCardComponent {
10501
11411
  this.decide = output();
10502
11412
  this.now = signal(Date.now(), ...(ngDevMode ? [{ debugName: "now" }] : []));
10503
11413
  this.submitted = signal(false, ...(ngDevMode ? [{ debugName: "submitted" }] : []));
11414
+ /** The decision this card sent last, so a proposal left for the approvers reads as such at once. */
11415
+ this.chosen = signal(null, ...(ngDevMode ? [{ debugName: "chosen" }] : []));
10504
11416
  /** The feedback question is expanded. */
10505
11417
  this.feedbackOpen = signal(false, ...(ngDevMode ? [{ debugName: "feedbackOpen" }] : []));
10506
11418
  /** The feedback was stored; the question thanks the person and the action disappears. */
@@ -10510,11 +11422,45 @@ class NhAssistantApprovalCardComponent {
10510
11422
  this.expired = computed(() => this.approval().status === 'expired' ||
10511
11423
  (this.approval().status === 'pending' && this.remainingMs() <= 0), ...(ngDevMode ? [{ debugName: "expired" }] : []));
10512
11424
  this.effectiveStatus = computed(() => this.expired() ? 'expired' : this.approval().status, ...(ngDevMode ? [{ debugName: "effectiveStatus" }] : []));
11425
+ /** Where a consulted agent's proposal comes from; `null` for the conversation's own proposals. */
11426
+ this.consultation = computed(() => this.approval().consultation ?? null, ...(ngDevMode ? [{ debugName: "consultation" }] : []));
11427
+ /**
11428
+ * The translation keys of the consulted agent's name: the name the assistant's status gives it,
11429
+ * then the convention `nh-assistant.agents.{id}.name`; the id when neither translates.
11430
+ */
11431
+ this.consultedAgentKeys = computed(() => {
11432
+ const consultation = this.consultation();
11433
+ if (!consultation) {
11434
+ return [];
11435
+ }
11436
+ const convention = `nh-assistant.agents.${consultation.agentId}.name`;
11437
+ const known = this.store?.agents().find(agent => agent.id === consultation.agentId)?.displayNameKey;
11438
+ return known && known !== convention ? [known, convention] : [convention];
11439
+ }, ...(ngDevMode ? [{ debugName: "consultedAgentKeys" }] : []));
11440
+ /** The subject the consulted agent was asked about, by name or else by id. */
11441
+ this.consultedSubject = computed(() => {
11442
+ const consultation = this.consultation();
11443
+ return consultation ? consultation.subjectDisplayName ?? consultation.subjectId : null;
11444
+ }, ...(ngDevMode ? [{ debugName: "consultedSubject" }] : []));
11445
+ /**
11446
+ * The user left the consulted agent's proposal for the subject's approvers: the server names the
11447
+ * proposal that waits there, or this card just sent the decision and it was accepted.
11448
+ */
11449
+ this.leftForApprovers = computed(() => {
11450
+ const consultation = this.consultation();
11451
+ return consultation !== null
11452
+ && (!!consultation.movedToApprovalId || (this.chosen() === 'leave' && this.approval().status !== 'pending'));
11453
+ }, ...(ngDevMode ? [{ debugName: "leftForApprovers" }] : []));
11454
+ /** The status the card shows: `moved` for a proposal left for the subject's approvers. */
11455
+ this.shownStatus = computed(() => this.leftForApprovers() ? 'moved' : this.effectiveStatus(), ...(ngDevMode ? [{ debugName: "shownStatus" }] : []));
10513
11456
  /**
10514
11457
  * Translation key of the status: the kind of a stale proposal, "replaced" for a superseded one,
10515
- * otherwise the status itself.
11458
+ * "left for approvers" for a consulted agent's proposal the user left, otherwise the status itself.
10516
11459
  */
10517
11460
  this.statusKey = computed(() => {
11461
+ if (this.leftForApprovers()) {
11462
+ return 'nh-assistant.approval.consultation.moved-status';
11463
+ }
10518
11464
  const kind = this.approval().staleKind;
10519
11465
  if (this.effectiveStatus() === 'stale' && kind) {
10520
11466
  return `nh-assistant.approval.stale-kind.${kind}`;
@@ -10530,6 +11476,9 @@ class NhAssistantApprovalCardComponent {
10530
11476
  this.actionable = computed(() => this.effectiveStatus() === 'pending', ...(ngDevMode ? [{ debugName: "actionable" }] : []));
10531
11477
  this.succeeded = computed(() => ['approved', 'executing', 'executed', 'resolved'].includes(this.effectiveStatus()), ...(ngDevMode ? [{ debugName: "succeeded" }] : []));
10532
11478
  this.disabled = computed(() => !this.actionable() || !this.canDecide() || this.deciding() || this.submitted(), ...(ngDevMode ? [{ debugName: "disabled" }] : []));
11479
+ /** The route at the subject the pending proposal may be left for; `null` when it can only be approved or rejected. */
11480
+ this.leaveRoute = computed(() => this.actionable() ? this.consultation()?.leaveRoute ?? null : null, ...(ngDevMode ? [{ debugName: "leaveRoute" }] : []));
11481
+ this.leaveHintId = `nh-assistant-approval-leave-${nextId$5++}`;
10533
11482
  this.countdown = computed(() => formatNhAssistantCountdown(this.remainingMs()), ...(ngDevMode ? [{ debugName: "countdown" }] : []));
10534
11483
  this.presentedSummary = computed(() => this.approval().presentation?.summary ?? null, ...(ngDevMode ? [{ debugName: "presentedSummary" }] : []));
10535
11484
  this.feedbackAvailable = computed(() => this.canGiveFeedback() && this.effectiveStatus() === 'rejected', ...(ngDevMode ? [{ debugName: "feedbackAvailable" }] : []));
@@ -10542,6 +11491,8 @@ class NhAssistantApprovalCardComponent {
10542
11491
  return 'nh-assistant.approval.waiting-for-approver';
10543
11492
  case 'other-approver':
10544
11493
  return 'nh-assistant.approval.waiting-for-other-approver';
11494
+ case 'asker':
11495
+ return 'nh-assistant.approval.waiting-for-asker';
10545
11496
  default:
10546
11497
  return this.decisionOwner() ? 'nh-assistant.approval.waiting-for' : 'nh-assistant.approval.waiting-for-participant';
10547
11498
  }
@@ -10567,11 +11518,13 @@ class NhAssistantApprovalCardComponent {
10567
11518
  }
10568
11519
  });
10569
11520
  }
11521
+ /** Emits a decision; `leave` only for a consulted agent's proposal with a route at the subject. */
10570
11522
  choose(decision) {
10571
- if (this.disabled()) {
11523
+ if (this.disabled() || (decision === 'leave' && this.leaveRoute() === null)) {
10572
11524
  return;
10573
11525
  }
10574
11526
  this.submitted.set(true);
11527
+ this.chosen.set(decision);
10575
11528
  this.decide.emit(decision);
10576
11529
  }
10577
11530
  toggleFeedback() {
@@ -10586,13 +11539,13 @@ class NhAssistantApprovalCardComponent {
10586
11539
  this.feedbackToggle()?.nativeElement.focus();
10587
11540
  }
10588
11541
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantApprovalCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
10589
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", type: NhAssistantApprovalCardComponent, isStandalone: true, selector: "nh-assistant-approval-card", inputs: { approval: { classPropertyName: "approval", publicName: "approval", isSignal: true, isRequired: true, transformFunction: null }, deciding: { classPropertyName: "deciding", publicName: "deciding", isSignal: true, isRequired: false, transformFunction: null }, canDecide: { classPropertyName: "canDecide", publicName: "canDecide", isSignal: true, isRequired: false, transformFunction: null }, decisionOwner: { classPropertyName: "decisionOwner", publicName: "decisionOwner", isSignal: true, isRequired: false, transformFunction: null }, waitingFor: { classPropertyName: "waitingFor", publicName: "waitingFor", isSignal: true, isRequired: false, transformFunction: null }, canGiveFeedback: { classPropertyName: "canGiveFeedback", publicName: "canGiveFeedback", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { decide: "decide" }, host: { properties: { "attr.data-status": "effectiveStatus()" } }, viewQueries: [{ propertyName: "feedbackToggle", first: true, predicate: ["feedbackToggle"], descendants: true, isSignal: true }], ngImport: i0, template: "<section class=\"card\" [attr.aria-label]=\"'nh-assistant.approval.title' | translate\">\n <header class=\"header\">\n <nh-assistant-icon [name]=\"actionable() ? 'warning' : succeeded() ? 'check' : 'x'\" />\n <h3>{{ 'nh-assistant.approval.title' | translate }}</h3>\n <span class=\"status\">{{ statusKey() | translate }}</span>\n </header>\n\n @if (staleExplanation(); as explanation) {\n <p class=\"stale-explanation\">{{ explanation }}</p>\n }\n\n @if (approval().presentation; as presentation) {\n <p class=\"tool-name\">{{ presentation.toolDisplayName }}</p>\n <p class=\"summary\">{{ presentation.summary }}</p>\n @if (presentation.fields.length > 0) {\n <dl class=\"presentation-fields\">\n @for (field of presentation.fields; track $index) {\n <div>\n <dt>{{ field.label }}</dt>\n <dd>{{ field.value }}</dd>\n </div>\n }\n </dl>\n }\n @if (presentation.notice) {\n <p class=\"notice\">{{ presentation.notice }}</p>\n }\n } @else {\n <p class=\"summary\">{{ 'nh-assistant.approval.fallback-summary' | translate }}</p>\n }\n\n @if (rationale(); as why) {\n <p class=\"rationale\">{{ 'nh-assistant.approval.rationale' | translate: { rationale: why } }}</p>\n }\n\n <details class=\"technical-details\">\n <summary>{{ 'nh-assistant.approval.technical-details' | translate }}</summary>\n <p class=\"label\">{{ 'nh-assistant.approval.tool-id' | translate }}</p>\n <code>{{ approval().toolId }}</code>\n @if (approval().targets.length > 0) {\n <p class=\"label\">{{ 'nh-assistant.approval.targets' | translate }}</p>\n <ul class=\"targets\">\n @for (target of approval().targets; track $index) {\n <li>{{ target }}</li>\n }\n </ul>\n }\n @if (approval().argumentsPreview) {\n <p class=\"label\">{{ 'nh-assistant.approval.arguments' | translate }}</p>\n <pre>{{ approval().argumentsPreview }}</pre>\n }\n </details>\n\n @if (actionable() && !canDecide()) {\n <p class=\"waiting-for-other\" role=\"status\">\n <nh-assistant-icon name=\"users\" />\n {{ waitingKey() | translate: { name: decisionOwner() } }}\n </p>\n } @else if (actionable()) {\n <p class=\"expiry\" aria-live=\"off\">\n <nh-assistant-icon name=\"clock\" />\n {{ 'nh-assistant.approval.expires-in' | translate: { time: countdown() } }}\n </p>\n <div class=\"actions\">\n <button\n class=\"reject\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.reject-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n (click)=\"choose('reject')\">\n {{ 'nh-assistant.approval.reject' | translate }}\n </button>\n <button\n class=\"approve\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.approve-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n (click)=\"choose('approve')\">\n {{ 'nh-assistant.approval.approve' | translate }}\n </button>\n </div>\n @if (deciding()) {\n <p class=\"deciding\" role=\"status\">{{ 'nh-assistant.approval.deciding' | translate }}</p>\n }\n } @else if (expired()) {\n <p class=\"expiry\">{{ 'nh-assistant.approval.expired' | translate }}</p>\n }\n\n @if (feedbackAvailable()) {\n @if (!feedbackSaved()) {\n <button\n #feedbackToggle\n class=\"feedback-toggle\"\n type=\"button\"\n [attr.aria-expanded]=\"feedbackOpen()\"\n [attr.aria-controls]=\"feedbackOpen() ? feedbackPanelId : null\"\n (click)=\"toggleFeedback()\">\n {{ 'nh-assistant.approval.give-feedback' | translate }}\n </button>\n }\n @if (feedbackOpen()) {\n <nh-assistant-proposal-feedback\n class=\"feedback\"\n [attr.id]=\"feedbackPanelId\"\n [approvalIds]=\"feedbackIds()\"\n (saved)=\"onFeedbackSaved()\"\n (dismissed)=\"onFeedbackDismissed()\" />\n }\n }\n</section>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:block;--nha-status-color: var(--nha-warning);--nha-status-background: var(--nha-warning-soft)}:host([data-status=approved]){--nha-status-color: var(--nha-success);--nha-status-background: var(--nha-success-soft)}:host([data-status=rejected]),:host([data-status=expired]),:host([data-status=stale]){--nha-status-color: var(--nha-text-muted);--nha-status-background: var(--nha-surface-strong)}.card{border:1px solid var(--nha-status-color);border-radius:var(--nha-radius-control);padding:12px;color:var(--nha-text);background:var(--nha-background)}.header{display:flex;align-items:center;gap:8px;color:var(--nha-status-color)}h3{flex:1;margin:0;color:var(--nha-text);font-size:14px;font-weight:700}.status{border-radius:999px;padding:2px 8px;color:var(--nha-status-color);background:var(--nha-status-background);font-size:11px;font-weight:700}.summary{margin:8px 0 0;font-size:14px;overflow-wrap:anywhere}.tool-name{margin:10px 0 0;font-size:12px;font-weight:700}.presentation-fields{display:grid;gap:6px;margin:10px 0 0}.presentation-fields div{display:grid;grid-template-columns:minmax(90px,.4fr) minmax(0,1fr);gap:8px}.presentation-fields dt{color:var(--nha-text-muted);font-size:12px;font-weight:700;overflow-wrap:anywhere}.presentation-fields dd{margin:0;overflow-wrap:anywhere;font-size:13px}.notice{margin:10px 0 0;color:var(--nha-text-muted);font-size:12px}.stale-explanation{margin:8px 0 0;border-radius:8px;padding:8px 10px;color:var(--nha-text);background:var(--nha-surface-strong);font-size:13px;overflow-wrap:anywhere}.rationale{margin:8px 0 0;color:var(--nha-text-muted);font-size:13px;overflow-wrap:anywhere}.technical-details{margin-top:10px;color:var(--nha-text-muted);font-size:12px}.technical-details summary{cursor:pointer;font-weight:700}.technical-details code{overflow-wrap:anywhere}.label{margin:10px 0 4px;color:var(--nha-text-muted);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em}.targets{margin:0;padding-left:18px;font-size:13px;overflow-wrap:anywhere}pre{max-height:160px;margin:0;overflow:auto;border-radius:8px;padding:8px;background:var(--nha-surface);font-family:var(--nha-mono);font-size:12px;white-space:pre-wrap;word-break:break-word}.expiry,.deciding,.waiting-for-other{display:flex;align-items:center;gap:6px;margin:10px 0 0;color:var(--nha-text-muted);font-size:12px;font-variant-numeric:tabular-nums}.actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;margin-top:12px}button{min-height:36px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 14px;font:inherit;font-size:13px;font-weight:700;cursor:pointer}button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}button:disabled{cursor:not-allowed;opacity:.55}.reject{color:var(--nha-text);background:transparent}.reject:hover:not(:disabled){background:var(--nha-surface-strong)}.approve{border-color:var(--nha-accent);color:var(--nha-accent-contrast);background:var(--nha-accent)}.approve:hover:not(:disabled){filter:brightness(1.12)}.feedback-toggle{display:inline-flex;min-height:0;margin-top:10px;border:0;border-radius:6px;padding:2px 0;color:var(--nha-text-muted);background:transparent;font-size:12px;font-weight:650;text-decoration:underline;text-underline-offset:2px}.feedback-toggle:hover:not(:disabled){color:var(--nha-text)}.feedback{display:block;margin-top:10px}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "component", type: NhAssistantProposalFeedbackComponent, selector: "nh-assistant-proposal-feedback", inputs: ["approvalIds", "initial"], outputs: ["saved", "dismissed"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11542
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", type: NhAssistantApprovalCardComponent, isStandalone: true, selector: "nh-assistant-approval-card", inputs: { approval: { classPropertyName: "approval", publicName: "approval", isSignal: true, isRequired: true, transformFunction: null }, deciding: { classPropertyName: "deciding", publicName: "deciding", isSignal: true, isRequired: false, transformFunction: null }, canDecide: { classPropertyName: "canDecide", publicName: "canDecide", isSignal: true, isRequired: false, transformFunction: null }, decisionOwner: { classPropertyName: "decisionOwner", publicName: "decisionOwner", isSignal: true, isRequired: false, transformFunction: null }, waitingFor: { classPropertyName: "waitingFor", publicName: "waitingFor", isSignal: true, isRequired: false, transformFunction: null }, canGiveFeedback: { classPropertyName: "canGiveFeedback", publicName: "canGiveFeedback", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { decide: "decide" }, host: { properties: { "attr.data-status": "shownStatus()" } }, viewQueries: [{ propertyName: "feedbackToggle", first: true, predicate: ["feedbackToggle"], descendants: true, isSignal: true }], ngImport: i0, template: "<section class=\"card\" [attr.aria-label]=\"'nh-assistant.approval.title' | translate\">\n <header class=\"header\">\n <nh-assistant-icon [name]=\"leftForApprovers() ? 'users' : actionable() ? 'warning' : succeeded() ? 'check' : 'x'\" />\n <h3>{{ 'nh-assistant.approval.title' | translate }}</h3>\n <span class=\"status\">{{ statusKey() | translate }}</span>\n </header>\n\n @if (staleExplanation(); as explanation) {\n <p class=\"stale-explanation\">{{ explanation }}</p>\n }\n\n <!-- Another agent the assistant consulted proposes this; the person who asked decides it here. -->\n @if (consultation(); as origin) {\n <p class=\"consultation-origin\" data-consultation-origin>\n <nh-assistant-icon name=\"users\" />\n <span>{{ 'nh-assistant.approval.consultation.origin' | translate: { agent: (consultedAgentKeys() | nhAssistantTranslate: origin.agentId), subject: consultedSubject() } }}</span>\n </p>\n }\n\n @if (approval().presentation; as presentation) {\n <p class=\"tool-name\">{{ presentation.toolDisplayName }}</p>\n <p class=\"summary\">{{ presentation.summary }}</p>\n @if (presentation.fields.length > 0) {\n <dl class=\"presentation-fields\">\n @for (field of presentation.fields; track $index) {\n <div>\n <dt>{{ field.label }}</dt>\n <dd>{{ field.value }}</dd>\n </div>\n }\n </dl>\n }\n @if (presentation.notice) {\n <p class=\"notice\">{{ presentation.notice }}</p>\n }\n } @else {\n <p class=\"summary\">{{ 'nh-assistant.approval.fallback-summary' | translate }}</p>\n }\n\n @if (rationale(); as why) {\n <p class=\"rationale\">{{ 'nh-assistant.approval.rationale' | translate: { rationale: why } }}</p>\n }\n\n @if (consultation(); as origin) {\n @if (origin.answer) {\n <details class=\"consultation-answer\" data-consultation-answer>\n <summary>{{ 'nh-assistant.approval.consultation.answer' | translate: { agent: (consultedAgentKeys() | nhAssistantTranslate: origin.agentId) } }}</summary>\n <p>{{ origin.answer }}</p>\n </details>\n }\n @if (leftForApprovers()) {\n <p class=\"consultation-moved\" role=\"status\" data-consultation-moved>\n <nh-assistant-icon name=\"users\" />\n <span>{{ 'nh-assistant.approval.consultation.moved' | translate: { subject: consultedSubject() } }}</span>\n </p>\n }\n }\n\n <details class=\"technical-details\">\n <summary>{{ 'nh-assistant.approval.technical-details' | translate }}</summary>\n <p class=\"label\">{{ 'nh-assistant.approval.tool-id' | translate }}</p>\n <code>{{ approval().toolId }}</code>\n @if (approval().targets.length > 0) {\n <p class=\"label\">{{ 'nh-assistant.approval.targets' | translate }}</p>\n <ul class=\"targets\">\n @for (target of approval().targets; track $index) {\n <li>{{ target }}</li>\n }\n </ul>\n }\n @if (approval().argumentsPreview) {\n <p class=\"label\">{{ 'nh-assistant.approval.arguments' | translate }}</p>\n <pre>{{ approval().argumentsPreview }}</pre>\n }\n </details>\n\n @if (actionable() && !canDecide()) {\n <p class=\"waiting-for-other\" role=\"status\">\n <nh-assistant-icon name=\"users\" />\n {{ waitingKey() | translate: { name: decisionOwner() } }}\n </p>\n } @else if (actionable()) {\n @if (consultation()) {\n <p class=\"consultation-rights\" data-consultation-rights>{{ 'nh-assistant.approval.consultation.own-rights' | translate }}</p>\n }\n <p class=\"expiry\" aria-live=\"off\">\n <nh-assistant-icon name=\"clock\" />\n {{ 'nh-assistant.approval.expires-in' | translate: { time: countdown() } }}\n </p>\n <div class=\"actions\">\n <button\n class=\"reject\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.reject-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n (click)=\"choose('reject')\">\n {{ 'nh-assistant.approval.reject' | translate }}\n </button>\n <button\n class=\"approve\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.approve-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n (click)=\"choose('approve')\">\n {{ 'nh-assistant.approval.approve' | translate }}\n </button>\n </div>\n <!-- The person may leave a consulted agent's proposal for the subject's approvers instead. -->\n @if (leaveRoute(); as route) {\n <div class=\"leave-for-approvers\">\n <p class=\"leave-explanation\" [id]=\"leaveHintId\">{{ 'nh-assistant.approval.consultation.leave-explain.' + route | translate: { subject: consultedSubject() } }}</p>\n <button\n class=\"leave\"\n type=\"button\"\n data-leave\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.consultation.leave-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n [attr.aria-describedby]=\"leaveHintId\"\n (click)=\"choose('leave')\">\n {{ 'nh-assistant.approval.consultation.leave' | translate }}\n </button>\n </div>\n }\n @if (deciding()) {\n <p class=\"deciding\" role=\"status\">{{ 'nh-assistant.approval.deciding' | translate }}</p>\n }\n } @else if (expired()) {\n <p class=\"expiry\">{{ 'nh-assistant.approval.expired' | translate }}</p>\n }\n\n @if (feedbackAvailable()) {\n @if (!feedbackSaved()) {\n <button\n #feedbackToggle\n class=\"feedback-toggle\"\n type=\"button\"\n [attr.aria-expanded]=\"feedbackOpen()\"\n [attr.aria-controls]=\"feedbackOpen() ? feedbackPanelId : null\"\n (click)=\"toggleFeedback()\">\n {{ 'nh-assistant.approval.give-feedback' | translate }}\n </button>\n }\n @if (feedbackOpen()) {\n <nh-assistant-proposal-feedback\n class=\"feedback\"\n [attr.id]=\"feedbackPanelId\"\n [approvalIds]=\"feedbackIds()\"\n (saved)=\"onFeedbackSaved()\"\n (dismissed)=\"onFeedbackDismissed()\" />\n }\n }\n</section>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:block;--nha-status-color: var(--nha-warning);--nha-status-background: var(--nha-warning-soft)}:host([data-status=approved]){--nha-status-color: var(--nha-success);--nha-status-background: var(--nha-success-soft)}:host([data-status=rejected]),:host([data-status=expired]),:host([data-status=stale]){--nha-status-color: var(--nha-text-muted);--nha-status-background: var(--nha-surface-strong)}.card{border:1px solid var(--nha-status-color);border-radius:var(--nha-radius-control);padding:12px;color:var(--nha-text);background:var(--nha-background)}.header{display:flex;align-items:center;gap:8px;color:var(--nha-status-color)}h3{flex:1;margin:0;color:var(--nha-text);font-size:14px;font-weight:700}.status{border-radius:999px;padding:2px 8px;color:var(--nha-status-color);background:var(--nha-status-background);font-size:11px;font-weight:700}.summary{margin:8px 0 0;font-size:14px;overflow-wrap:anywhere}.tool-name{margin:10px 0 0;font-size:12px;font-weight:700}.presentation-fields{display:grid;gap:6px;margin:10px 0 0}.presentation-fields div{display:grid;grid-template-columns:minmax(90px,.4fr) minmax(0,1fr);gap:8px}.presentation-fields dt{color:var(--nha-text-muted);font-size:12px;font-weight:700;overflow-wrap:anywhere}.presentation-fields dd{margin:0;overflow-wrap:anywhere;font-size:13px}.notice{margin:10px 0 0;color:var(--nha-text-muted);font-size:12px}.stale-explanation{margin:8px 0 0;border-radius:8px;padding:8px 10px;color:var(--nha-text);background:var(--nha-surface-strong);font-size:13px;overflow-wrap:anywhere}.consultation-origin,.consultation-moved{display:flex;align-items:flex-start;gap:6px;margin:8px 0 0;color:var(--nha-text-muted);font-size:13px;overflow-wrap:anywhere}.consultation-moved{border-radius:8px;padding:8px 10px;color:var(--nha-text);background:var(--nha-surface-strong)}.consultation-answer{margin-top:8px;font-size:13px}.consultation-answer summary{cursor:pointer;color:var(--nha-text-muted);font-size:12px;font-weight:700}.consultation-answer summary:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.consultation-answer p{max-height:200px;margin:6px 0 0;overflow:auto;border-radius:8px;padding:8px;background:var(--nha-surface);white-space:pre-wrap;overflow-wrap:anywhere}.consultation-rights{margin:10px 0 0;color:var(--nha-text-muted);font-size:12px}.rationale{margin:8px 0 0;color:var(--nha-text-muted);font-size:13px;overflow-wrap:anywhere}.technical-details{margin-top:10px;color:var(--nha-text-muted);font-size:12px}.technical-details summary{cursor:pointer;font-weight:700}.technical-details code{overflow-wrap:anywhere}.label{margin:10px 0 4px;color:var(--nha-text-muted);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em}.targets{margin:0;padding-left:18px;font-size:13px;overflow-wrap:anywhere}pre{max-height:160px;margin:0;overflow:auto;border-radius:8px;padding:8px;background:var(--nha-surface);font-family:var(--nha-mono);font-size:12px;white-space:pre-wrap;word-break:break-word}.expiry,.deciding,.waiting-for-other{display:flex;align-items:center;gap:6px;margin:10px 0 0;color:var(--nha-text-muted);font-size:12px;font-variant-numeric:tabular-nums}.actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;margin-top:12px}button{min-height:36px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 14px;font:inherit;font-size:13px;font-weight:700;cursor:pointer}button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}button:disabled{cursor:not-allowed;opacity:.55}.leave-for-approvers{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:8px;margin-top:10px}.leave-explanation{flex:1 1 200px;margin:0;color:var(--nha-text-muted);font-size:12px}.reject,.leave{color:var(--nha-text);background:transparent}.reject:hover:not(:disabled),.leave:hover:not(:disabled){background:var(--nha-surface-strong)}.approve{border-color:var(--nha-accent);color:var(--nha-accent-contrast);background:var(--nha-accent)}.approve:hover:not(:disabled){filter:brightness(1.12)}.feedback-toggle{display:inline-flex;min-height:0;margin-top:10px;border:0;border-radius:6px;padding:2px 0;color:var(--nha-text-muted);background:transparent;font-size:12px;font-weight:650;text-decoration:underline;text-underline-offset:2px}.feedback-toggle:hover:not(:disabled){color:var(--nha-text)}.feedback{display:block;margin-top:10px}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "component", type: NhAssistantProposalFeedbackComponent, selector: "nh-assistant-proposal-feedback", inputs: ["approvalIds", "initial"], outputs: ["saved", "dismissed"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10590
11543
  }
10591
11544
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantApprovalCardComponent, decorators: [{
10592
11545
  type: Component,
10593
- args: [{ selector: 'nh-assistant-approval-card', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent, NhAssistantProposalFeedbackComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
10594
- '[attr.data-status]': 'effectiveStatus()'
10595
- }, template: "<section class=\"card\" [attr.aria-label]=\"'nh-assistant.approval.title' | translate\">\n <header class=\"header\">\n <nh-assistant-icon [name]=\"actionable() ? 'warning' : succeeded() ? 'check' : 'x'\" />\n <h3>{{ 'nh-assistant.approval.title' | translate }}</h3>\n <span class=\"status\">{{ statusKey() | translate }}</span>\n </header>\n\n @if (staleExplanation(); as explanation) {\n <p class=\"stale-explanation\">{{ explanation }}</p>\n }\n\n @if (approval().presentation; as presentation) {\n <p class=\"tool-name\">{{ presentation.toolDisplayName }}</p>\n <p class=\"summary\">{{ presentation.summary }}</p>\n @if (presentation.fields.length > 0) {\n <dl class=\"presentation-fields\">\n @for (field of presentation.fields; track $index) {\n <div>\n <dt>{{ field.label }}</dt>\n <dd>{{ field.value }}</dd>\n </div>\n }\n </dl>\n }\n @if (presentation.notice) {\n <p class=\"notice\">{{ presentation.notice }}</p>\n }\n } @else {\n <p class=\"summary\">{{ 'nh-assistant.approval.fallback-summary' | translate }}</p>\n }\n\n @if (rationale(); as why) {\n <p class=\"rationale\">{{ 'nh-assistant.approval.rationale' | translate: { rationale: why } }}</p>\n }\n\n <details class=\"technical-details\">\n <summary>{{ 'nh-assistant.approval.technical-details' | translate }}</summary>\n <p class=\"label\">{{ 'nh-assistant.approval.tool-id' | translate }}</p>\n <code>{{ approval().toolId }}</code>\n @if (approval().targets.length > 0) {\n <p class=\"label\">{{ 'nh-assistant.approval.targets' | translate }}</p>\n <ul class=\"targets\">\n @for (target of approval().targets; track $index) {\n <li>{{ target }}</li>\n }\n </ul>\n }\n @if (approval().argumentsPreview) {\n <p class=\"label\">{{ 'nh-assistant.approval.arguments' | translate }}</p>\n <pre>{{ approval().argumentsPreview }}</pre>\n }\n </details>\n\n @if (actionable() && !canDecide()) {\n <p class=\"waiting-for-other\" role=\"status\">\n <nh-assistant-icon name=\"users\" />\n {{ waitingKey() | translate: { name: decisionOwner() } }}\n </p>\n } @else if (actionable()) {\n <p class=\"expiry\" aria-live=\"off\">\n <nh-assistant-icon name=\"clock\" />\n {{ 'nh-assistant.approval.expires-in' | translate: { time: countdown() } }}\n </p>\n <div class=\"actions\">\n <button\n class=\"reject\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.reject-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n (click)=\"choose('reject')\">\n {{ 'nh-assistant.approval.reject' | translate }}\n </button>\n <button\n class=\"approve\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.approve-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n (click)=\"choose('approve')\">\n {{ 'nh-assistant.approval.approve' | translate }}\n </button>\n </div>\n @if (deciding()) {\n <p class=\"deciding\" role=\"status\">{{ 'nh-assistant.approval.deciding' | translate }}</p>\n }\n } @else if (expired()) {\n <p class=\"expiry\">{{ 'nh-assistant.approval.expired' | translate }}</p>\n }\n\n @if (feedbackAvailable()) {\n @if (!feedbackSaved()) {\n <button\n #feedbackToggle\n class=\"feedback-toggle\"\n type=\"button\"\n [attr.aria-expanded]=\"feedbackOpen()\"\n [attr.aria-controls]=\"feedbackOpen() ? feedbackPanelId : null\"\n (click)=\"toggleFeedback()\">\n {{ 'nh-assistant.approval.give-feedback' | translate }}\n </button>\n }\n @if (feedbackOpen()) {\n <nh-assistant-proposal-feedback\n class=\"feedback\"\n [attr.id]=\"feedbackPanelId\"\n [approvalIds]=\"feedbackIds()\"\n (saved)=\"onFeedbackSaved()\"\n (dismissed)=\"onFeedbackDismissed()\" />\n }\n }\n</section>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:block;--nha-status-color: var(--nha-warning);--nha-status-background: var(--nha-warning-soft)}:host([data-status=approved]){--nha-status-color: var(--nha-success);--nha-status-background: var(--nha-success-soft)}:host([data-status=rejected]),:host([data-status=expired]),:host([data-status=stale]){--nha-status-color: var(--nha-text-muted);--nha-status-background: var(--nha-surface-strong)}.card{border:1px solid var(--nha-status-color);border-radius:var(--nha-radius-control);padding:12px;color:var(--nha-text);background:var(--nha-background)}.header{display:flex;align-items:center;gap:8px;color:var(--nha-status-color)}h3{flex:1;margin:0;color:var(--nha-text);font-size:14px;font-weight:700}.status{border-radius:999px;padding:2px 8px;color:var(--nha-status-color);background:var(--nha-status-background);font-size:11px;font-weight:700}.summary{margin:8px 0 0;font-size:14px;overflow-wrap:anywhere}.tool-name{margin:10px 0 0;font-size:12px;font-weight:700}.presentation-fields{display:grid;gap:6px;margin:10px 0 0}.presentation-fields div{display:grid;grid-template-columns:minmax(90px,.4fr) minmax(0,1fr);gap:8px}.presentation-fields dt{color:var(--nha-text-muted);font-size:12px;font-weight:700;overflow-wrap:anywhere}.presentation-fields dd{margin:0;overflow-wrap:anywhere;font-size:13px}.notice{margin:10px 0 0;color:var(--nha-text-muted);font-size:12px}.stale-explanation{margin:8px 0 0;border-radius:8px;padding:8px 10px;color:var(--nha-text);background:var(--nha-surface-strong);font-size:13px;overflow-wrap:anywhere}.rationale{margin:8px 0 0;color:var(--nha-text-muted);font-size:13px;overflow-wrap:anywhere}.technical-details{margin-top:10px;color:var(--nha-text-muted);font-size:12px}.technical-details summary{cursor:pointer;font-weight:700}.technical-details code{overflow-wrap:anywhere}.label{margin:10px 0 4px;color:var(--nha-text-muted);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em}.targets{margin:0;padding-left:18px;font-size:13px;overflow-wrap:anywhere}pre{max-height:160px;margin:0;overflow:auto;border-radius:8px;padding:8px;background:var(--nha-surface);font-family:var(--nha-mono);font-size:12px;white-space:pre-wrap;word-break:break-word}.expiry,.deciding,.waiting-for-other{display:flex;align-items:center;gap:6px;margin:10px 0 0;color:var(--nha-text-muted);font-size:12px;font-variant-numeric:tabular-nums}.actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;margin-top:12px}button{min-height:36px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 14px;font:inherit;font-size:13px;font-weight:700;cursor:pointer}button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}button:disabled{cursor:not-allowed;opacity:.55}.reject{color:var(--nha-text);background:transparent}.reject:hover:not(:disabled){background:var(--nha-surface-strong)}.approve{border-color:var(--nha-accent);color:var(--nha-accent-contrast);background:var(--nha-accent)}.approve:hover:not(:disabled){filter:brightness(1.12)}.feedback-toggle{display:inline-flex;min-height:0;margin-top:10px;border:0;border-radius:6px;padding:2px 0;color:var(--nha-text-muted);background:transparent;font-size:12px;font-weight:650;text-decoration:underline;text-underline-offset:2px}.feedback-toggle:hover:not(:disabled){color:var(--nha-text)}.feedback{display:block;margin-top:10px}\n"] }]
11546
+ args: [{ selector: 'nh-assistant-approval-card', standalone: true, imports: [TranslatePipe, NhAssistantTranslatePipe, NhAssistantIconComponent, NhAssistantProposalFeedbackComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
11547
+ '[attr.data-status]': 'shownStatus()'
11548
+ }, template: "<section class=\"card\" [attr.aria-label]=\"'nh-assistant.approval.title' | translate\">\n <header class=\"header\">\n <nh-assistant-icon [name]=\"leftForApprovers() ? 'users' : actionable() ? 'warning' : succeeded() ? 'check' : 'x'\" />\n <h3>{{ 'nh-assistant.approval.title' | translate }}</h3>\n <span class=\"status\">{{ statusKey() | translate }}</span>\n </header>\n\n @if (staleExplanation(); as explanation) {\n <p class=\"stale-explanation\">{{ explanation }}</p>\n }\n\n <!-- Another agent the assistant consulted proposes this; the person who asked decides it here. -->\n @if (consultation(); as origin) {\n <p class=\"consultation-origin\" data-consultation-origin>\n <nh-assistant-icon name=\"users\" />\n <span>{{ 'nh-assistant.approval.consultation.origin' | translate: { agent: (consultedAgentKeys() | nhAssistantTranslate: origin.agentId), subject: consultedSubject() } }}</span>\n </p>\n }\n\n @if (approval().presentation; as presentation) {\n <p class=\"tool-name\">{{ presentation.toolDisplayName }}</p>\n <p class=\"summary\">{{ presentation.summary }}</p>\n @if (presentation.fields.length > 0) {\n <dl class=\"presentation-fields\">\n @for (field of presentation.fields; track $index) {\n <div>\n <dt>{{ field.label }}</dt>\n <dd>{{ field.value }}</dd>\n </div>\n }\n </dl>\n }\n @if (presentation.notice) {\n <p class=\"notice\">{{ presentation.notice }}</p>\n }\n } @else {\n <p class=\"summary\">{{ 'nh-assistant.approval.fallback-summary' | translate }}</p>\n }\n\n @if (rationale(); as why) {\n <p class=\"rationale\">{{ 'nh-assistant.approval.rationale' | translate: { rationale: why } }}</p>\n }\n\n @if (consultation(); as origin) {\n @if (origin.answer) {\n <details class=\"consultation-answer\" data-consultation-answer>\n <summary>{{ 'nh-assistant.approval.consultation.answer' | translate: { agent: (consultedAgentKeys() | nhAssistantTranslate: origin.agentId) } }}</summary>\n <p>{{ origin.answer }}</p>\n </details>\n }\n @if (leftForApprovers()) {\n <p class=\"consultation-moved\" role=\"status\" data-consultation-moved>\n <nh-assistant-icon name=\"users\" />\n <span>{{ 'nh-assistant.approval.consultation.moved' | translate: { subject: consultedSubject() } }}</span>\n </p>\n }\n }\n\n <details class=\"technical-details\">\n <summary>{{ 'nh-assistant.approval.technical-details' | translate }}</summary>\n <p class=\"label\">{{ 'nh-assistant.approval.tool-id' | translate }}</p>\n <code>{{ approval().toolId }}</code>\n @if (approval().targets.length > 0) {\n <p class=\"label\">{{ 'nh-assistant.approval.targets' | translate }}</p>\n <ul class=\"targets\">\n @for (target of approval().targets; track $index) {\n <li>{{ target }}</li>\n }\n </ul>\n }\n @if (approval().argumentsPreview) {\n <p class=\"label\">{{ 'nh-assistant.approval.arguments' | translate }}</p>\n <pre>{{ approval().argumentsPreview }}</pre>\n }\n </details>\n\n @if (actionable() && !canDecide()) {\n <p class=\"waiting-for-other\" role=\"status\">\n <nh-assistant-icon name=\"users\" />\n {{ waitingKey() | translate: { name: decisionOwner() } }}\n </p>\n } @else if (actionable()) {\n @if (consultation()) {\n <p class=\"consultation-rights\" data-consultation-rights>{{ 'nh-assistant.approval.consultation.own-rights' | translate }}</p>\n }\n <p class=\"expiry\" aria-live=\"off\">\n <nh-assistant-icon name=\"clock\" />\n {{ 'nh-assistant.approval.expires-in' | translate: { time: countdown() } }}\n </p>\n <div class=\"actions\">\n <button\n class=\"reject\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.reject-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n (click)=\"choose('reject')\">\n {{ 'nh-assistant.approval.reject' | translate }}\n </button>\n <button\n class=\"approve\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.approve-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n (click)=\"choose('approve')\">\n {{ 'nh-assistant.approval.approve' | translate }}\n </button>\n </div>\n <!-- The person may leave a consulted agent's proposal for the subject's approvers instead. -->\n @if (leaveRoute(); as route) {\n <div class=\"leave-for-approvers\">\n <p class=\"leave-explanation\" [id]=\"leaveHintId\">{{ 'nh-assistant.approval.consultation.leave-explain.' + route | translate: { subject: consultedSubject() } }}</p>\n <button\n class=\"leave\"\n type=\"button\"\n data-leave\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.consultation.leave-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n [attr.aria-describedby]=\"leaveHintId\"\n (click)=\"choose('leave')\">\n {{ 'nh-assistant.approval.consultation.leave' | translate }}\n </button>\n </div>\n }\n @if (deciding()) {\n <p class=\"deciding\" role=\"status\">{{ 'nh-assistant.approval.deciding' | translate }}</p>\n }\n } @else if (expired()) {\n <p class=\"expiry\">{{ 'nh-assistant.approval.expired' | translate }}</p>\n }\n\n @if (feedbackAvailable()) {\n @if (!feedbackSaved()) {\n <button\n #feedbackToggle\n class=\"feedback-toggle\"\n type=\"button\"\n [attr.aria-expanded]=\"feedbackOpen()\"\n [attr.aria-controls]=\"feedbackOpen() ? feedbackPanelId : null\"\n (click)=\"toggleFeedback()\">\n {{ 'nh-assistant.approval.give-feedback' | translate }}\n </button>\n }\n @if (feedbackOpen()) {\n <nh-assistant-proposal-feedback\n class=\"feedback\"\n [attr.id]=\"feedbackPanelId\"\n [approvalIds]=\"feedbackIds()\"\n (saved)=\"onFeedbackSaved()\"\n (dismissed)=\"onFeedbackDismissed()\" />\n }\n }\n</section>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:block;--nha-status-color: var(--nha-warning);--nha-status-background: var(--nha-warning-soft)}:host([data-status=approved]){--nha-status-color: var(--nha-success);--nha-status-background: var(--nha-success-soft)}:host([data-status=rejected]),:host([data-status=expired]),:host([data-status=stale]){--nha-status-color: var(--nha-text-muted);--nha-status-background: var(--nha-surface-strong)}.card{border:1px solid var(--nha-status-color);border-radius:var(--nha-radius-control);padding:12px;color:var(--nha-text);background:var(--nha-background)}.header{display:flex;align-items:center;gap:8px;color:var(--nha-status-color)}h3{flex:1;margin:0;color:var(--nha-text);font-size:14px;font-weight:700}.status{border-radius:999px;padding:2px 8px;color:var(--nha-status-color);background:var(--nha-status-background);font-size:11px;font-weight:700}.summary{margin:8px 0 0;font-size:14px;overflow-wrap:anywhere}.tool-name{margin:10px 0 0;font-size:12px;font-weight:700}.presentation-fields{display:grid;gap:6px;margin:10px 0 0}.presentation-fields div{display:grid;grid-template-columns:minmax(90px,.4fr) minmax(0,1fr);gap:8px}.presentation-fields dt{color:var(--nha-text-muted);font-size:12px;font-weight:700;overflow-wrap:anywhere}.presentation-fields dd{margin:0;overflow-wrap:anywhere;font-size:13px}.notice{margin:10px 0 0;color:var(--nha-text-muted);font-size:12px}.stale-explanation{margin:8px 0 0;border-radius:8px;padding:8px 10px;color:var(--nha-text);background:var(--nha-surface-strong);font-size:13px;overflow-wrap:anywhere}.consultation-origin,.consultation-moved{display:flex;align-items:flex-start;gap:6px;margin:8px 0 0;color:var(--nha-text-muted);font-size:13px;overflow-wrap:anywhere}.consultation-moved{border-radius:8px;padding:8px 10px;color:var(--nha-text);background:var(--nha-surface-strong)}.consultation-answer{margin-top:8px;font-size:13px}.consultation-answer summary{cursor:pointer;color:var(--nha-text-muted);font-size:12px;font-weight:700}.consultation-answer summary:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.consultation-answer p{max-height:200px;margin:6px 0 0;overflow:auto;border-radius:8px;padding:8px;background:var(--nha-surface);white-space:pre-wrap;overflow-wrap:anywhere}.consultation-rights{margin:10px 0 0;color:var(--nha-text-muted);font-size:12px}.rationale{margin:8px 0 0;color:var(--nha-text-muted);font-size:13px;overflow-wrap:anywhere}.technical-details{margin-top:10px;color:var(--nha-text-muted);font-size:12px}.technical-details summary{cursor:pointer;font-weight:700}.technical-details code{overflow-wrap:anywhere}.label{margin:10px 0 4px;color:var(--nha-text-muted);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em}.targets{margin:0;padding-left:18px;font-size:13px;overflow-wrap:anywhere}pre{max-height:160px;margin:0;overflow:auto;border-radius:8px;padding:8px;background:var(--nha-surface);font-family:var(--nha-mono);font-size:12px;white-space:pre-wrap;word-break:break-word}.expiry,.deciding,.waiting-for-other{display:flex;align-items:center;gap:6px;margin:10px 0 0;color:var(--nha-text-muted);font-size:12px;font-variant-numeric:tabular-nums}.actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;margin-top:12px}button{min-height:36px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 14px;font:inherit;font-size:13px;font-weight:700;cursor:pointer}button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}button:disabled{cursor:not-allowed;opacity:.55}.leave-for-approvers{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:8px;margin-top:10px}.leave-explanation{flex:1 1 200px;margin:0;color:var(--nha-text-muted);font-size:12px}.reject,.leave{color:var(--nha-text);background:transparent}.reject:hover:not(:disabled),.leave:hover:not(:disabled){background:var(--nha-surface-strong)}.approve{border-color:var(--nha-accent);color:var(--nha-accent-contrast);background:var(--nha-accent)}.approve:hover:not(:disabled){filter:brightness(1.12)}.feedback-toggle{display:inline-flex;min-height:0;margin-top:10px;border:0;border-radius:6px;padding:2px 0;color:var(--nha-text-muted);background:transparent;font-size:12px;font-weight:650;text-decoration:underline;text-underline-offset:2px}.feedback-toggle:hover:not(:disabled){color:var(--nha-text)}.feedback{display:block;margin-top:10px}\n"] }]
10596
11549
  }], ctorParameters: () => [], propDecorators: { approval: [{ type: i0.Input, args: [{ isSignal: true, alias: "approval", required: true }] }], deciding: [{ type: i0.Input, args: [{ isSignal: true, alias: "deciding", required: false }] }], canDecide: [{ type: i0.Input, args: [{ isSignal: true, alias: "canDecide", required: false }] }], decisionOwner: [{ type: i0.Input, args: [{ isSignal: true, alias: "decisionOwner", required: false }] }], waitingFor: [{ type: i0.Input, args: [{ isSignal: true, alias: "waitingFor", required: false }] }], canGiveFeedback: [{ type: i0.Input, args: [{ isSignal: true, alias: "canGiveFeedback", required: false }] }], decide: [{ type: i0.Output, args: ["decide"] }], feedbackToggle: [{ type: i0.ViewChild, args: ['feedbackToggle', { isSignal: true }] }] } });
10597
11550
 
10598
11551
  let nextId$4 = 0;
@@ -10774,9 +11727,12 @@ const stickToBottomDistance = 120;
10774
11727
  * The messages of a conversation as a live log. Assistant text renders as sanitized
10775
11728
  * Markdown, consecutive tool calls as one collapsible group that counts along, and
10776
11729
  * approvals as cards. The files a person attached show by name, type and size. Messages a
10777
- * trigger wrote into an agent thread read as events, not as a person. While a turn runs
10778
- * without visible progress, a working indicator shows the elapsed time. The thread follows
10779
- * new content while the user is at the bottom.
11730
+ * trigger wrote into an agent thread read as events, not as a person, and a message the application
11731
+ * posted reads as the application's, with its sender, title, Markdown text and link, and a label when
11732
+ * the assistant follows it as an instruction. While a turn runs without visible progress, a working
11733
+ * indicator shows the elapsed time: under the answer `writingMessageId` names, also when the
11734
+ * application's or another person's message arrived after it, and at the end without it. The thread
11735
+ * follows new content while the user is at the bottom.
10780
11736
  */
10781
11737
  class NhAssistantThreadComponent {
10782
11738
  /** The type and size of an attached file, in the current language. */
@@ -10824,6 +11780,13 @@ class NhAssistantThreadComponent {
10824
11780
  * card of a rejected proposal ask what was wrong.
10825
11781
  */
10826
11782
  this.approvalRights = input(null, ...(ngDevMode ? [{ debugName: "approvalRights" }] : []));
11783
+ /**
11784
+ * The answer the running turn writes, for example `NhAssistantStore.writingMessageId`. The working
11785
+ * indicator stays under that answer, also when the application's or a colleague's message arrives
11786
+ * after it during the turn. Without it, or when it names no message of `messages`, the indicator
11787
+ * shows at the end of the thread.
11788
+ */
11789
+ this.writingMessageId = input(null, ...(ngDevMode ? [{ debugName: "writingMessageId" }] : []));
10827
11790
  /** The decision about an approval card, without the approval it belongs to. */
10828
11791
  this.approvalDecision = output();
10829
11792
  /** The decision about an approval card together with its approval id. */
@@ -10839,14 +11802,16 @@ class NhAssistantThreadComponent {
10839
11802
  /**
10840
11803
  * True while the turn runs and nothing else shows progress: before the first part and
10841
11804
  * between tool calls while the model decides what to do next. Streaming text and a
10842
- * running tool group show their own progress; an approval waits for the user.
11805
+ * running tool group show their own progress; an approval waits for the user. With
11806
+ * `writingMessageId`, the answer it names decides, as if the messages after it were not there.
10843
11807
  */
10844
11808
  this.working = computed(() => {
10845
11809
  if (!this.streaming()) {
10846
11810
  return false;
10847
11811
  }
10848
- const last = this.messages()[this.messages().length - 1];
10849
- if (!last || last.role === 'user' || last.parts.length === 0) {
11812
+ const messages = this.messages();
11813
+ const last = this.answerBeingWritten() ?? messages[messages.length - 1];
11814
+ if (!last || last.role === 'user' || last.role === 'application' || last.parts.length === 0) {
10850
11815
  return true;
10851
11816
  }
10852
11817
  const lastPart = last.parts[last.parts.length - 1];
@@ -10854,6 +11819,19 @@ class NhAssistantThreadComponent {
10854
11819
  && lastPart.status !== 'running'
10855
11820
  && lastPart.status !== 'awaiting-approval';
10856
11821
  }, ...(ngDevMode ? [{ debugName: "working" }] : []));
11822
+ /**
11823
+ * The message in whose row the working indicator shows: the answer `writingMessageId` names.
11824
+ * `null` puts the indicator at the end of the thread.
11825
+ */
11826
+ this.workingAfterId = computed(() => this.answerBeingWritten()?.id ?? null, ...(ngDevMode ? [{ debugName: "workingAfterId" }] : []));
11827
+ /** While a turn runs: the answer `writingMessageId` names, when `messages` holds it. */
11828
+ this.answerBeingWritten = computed(() => {
11829
+ const writingMessageId = this.writingMessageId();
11830
+ if (!this.streaming() || !writingMessageId) {
11831
+ return null;
11832
+ }
11833
+ return this.messages().find(message => message.id === writingMessageId) ?? null;
11834
+ }, ...(ngDevMode ? [{ debugName: "answerBeingWritten" }] : []));
10857
11835
  /** Whole seconds since the running turn started. */
10858
11836
  this.elapsedSeconds = computed(() => {
10859
11837
  const startedAt = this.turnStartedAt();
@@ -10895,6 +11873,10 @@ class NhAssistantThreadComponent {
10895
11873
  isTrigger(message) {
10896
11874
  return message.role === 'user' && message.authorKind === 'trigger';
10897
11875
  }
11876
+ /** True for a message the application posted into the conversation. */
11877
+ isApplication(message) {
11878
+ return message.role === 'application';
11879
+ }
10898
11880
  /** True for an assistant message the agent's rules wrote without a model. */
10899
11881
  isRule(message) {
10900
11882
  return message.role === 'assistant' && message.authorKind === 'rule';
@@ -10939,7 +11921,7 @@ class NhAssistantThreadComponent {
10939
11921
  }
10940
11922
  }
10941
11923
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantThreadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
10942
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", type: NhAssistantThreadComponent, isStandalone: true, selector: "nh-assistant-thread", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, streaming: { classPropertyName: "streaming", publicName: "streaming", isSignal: true, isRequired: false, transformFunction: null }, deciding: { classPropertyName: "deciding", publicName: "deciding", isSignal: true, isRequired: false, transformFunction: null }, markdown: { classPropertyName: "markdown", publicName: "markdown", isSignal: true, isRequired: false, transformFunction: null }, members: { classPropertyName: "members", publicName: "members", isSignal: true, isRequired: false, transformFunction: null }, currentActorId: { classPropertyName: "currentActorId", publicName: "currentActorId", isSignal: true, isRequired: false, transformFunction: null }, canDecide: { classPropertyName: "canDecide", publicName: "canDecide", isSignal: true, isRequired: false, transformFunction: null }, decisionOwner: { classPropertyName: "decisionOwner", publicName: "decisionOwner", isSignal: true, isRequired: false, transformFunction: null }, approvalRights: { classPropertyName: "approvalRights", publicName: "approvalRights", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { approvalDecision: "approvalDecision", approvalChoice: "approvalChoice", retryMessage: "retryMessage" }, viewQueries: [{ propertyName: "scroller", first: true, predicate: ["scroller"], descendants: true, isSignal: true }, { propertyName: "viewport", first: true, predicate: CdkVirtualScrollViewport, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"log\" role=\"log\" aria-live=\"polite\" [attr.aria-label]=\"'nh-assistant.thread.label' | translate\" [attr.aria-busy]=\"streaming()\">\n @if (virtual()) {\n <cdk-virtual-scroll-viewport class=\"scroller\" itemSize=\"120\" minBufferPx=\"1200\" maxBufferPx=\"2400\">\n <div class=\"row\" *cdkVirtualFor=\"let message of messages(); trackBy: trackMessage\">\n <ng-container [ngTemplateOutlet]=\"messageTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message }\" />\n </div>\n @if (working()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </cdk-virtual-scroll-viewport>\n } @else {\n <div class=\"scroller\" #scroller tabindex=\"0\">\n @for (message of messages(); track message.id) {\n <div class=\"row\">\n <ng-container [ngTemplateOutlet]=\"messageTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message }\" />\n </div>\n }\n @if (working()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </div>\n }\n</div>\n\n<ng-template #messageTemplate let-message>\n <article\n class=\"message\"\n [attr.data-role]=\"message.role\"\n [attr.data-others]=\"isOthers(message) ? 'true' : null\"\n [attr.data-author]=\"isTrigger(message) ? 'trigger' : isRule(message) ? 'rule' : null\">\n @if (isTrigger(message)) {\n <p class=\"author\">{{ 'nh-assistant.thread.trigger' | translate }}</p>\n } @else if (isRule(message)) {\n <p class=\"author\">{{ 'nh-assistant.thread.rule' | translate }}</p>\n } @else if (isOthers(message)) {\n <p class=\"author\">{{ authorName(message) ?? ('nh-assistant.thread.participant' | translate) }}</p>\n } @else {\n <p class=\"author\">{{ 'nh-assistant.thread.' + (message.role === 'user' ? 'you' : message.role) | translate }}</p>\n }\n @for (group of message.parts | nhAssistantPartGroups; track group.key) {\n @switch (group.kind) {\n @case ('text') {\n @if (message.role === 'assistant' && markdown()) {\n <div class=\"bubble markdown\" [innerHTML]=\"group.part.text | nhAssistantMarkdown\"></div>\n } @else {\n <div class=\"bubble plain\">{{ group.part.text }}</div>\n }\n }\n @case ('tools') {\n <nh-assistant-tool-call-group class=\"part\" [calls]=\"group.calls\" />\n }\n @case ('files') {\n <ul class=\"files\" [attr.aria-label]=\"'nh-assistant.thread.files' | translate\">\n @for (file of group.files; track $index) {\n <li class=\"file\">\n <nh-assistant-icon class=\"file-icon\" name=\"file\" />\n <span class=\"file-name\" [attr.title]=\"file.name\">{{ file.name }}</span>\n <span class=\"file-meta\">{{ 'nh-assistant.thread.file-meta' | translate: fileMeta(file) }}</span>\n </li>\n }\n </ul>\n }\n @case ('connection') {\n <nh-assistant-connection-required\n class=\"part\"\n [part]=\"group.part\"\n [canRetry]=\"!streaming()\"\n (retry)=\"retryAfterConnect(message)\" />\n }\n @case ('approval') {\n <nh-assistant-approval-card\n class=\"part\"\n [approval]=\"group.part\"\n [deciding]=\"deciding()\"\n [canDecide]=\"rightFor(group.part).canDecide\"\n [decisionOwner]=\"rightFor(group.part).decisionOwner\"\n [waitingFor]=\"rightFor(group.part).waitingFor\"\n [canGiveFeedback]=\"rightFor(group.part).canGiveFeedback === true\"\n (decide)=\"choose(group.part, $event)\" />\n }\n }\n }\n </article>\n</ng-template>\n\n<ng-template #workingTemplate>\n <p class=\"working\" role=\"status\">\n <span class=\"dots\" aria-hidden=\"true\"><span></span><span></span><span></span></span>\n {{ 'nh-assistant.thread.working' | translate }}\n @if (elapsedSeconds() > 0) {\n <span class=\"elapsed\">{{ 'nh-assistant.thread.elapsed' | translate: { seconds: elapsedSeconds() } }}</span>\n }\n </p>\n</ng-template>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:flex;flex-direction:column;min-height:0;color:var(--nha-text)}.log{display:flex;flex:1;flex-direction:column;min-height:0}.scroller{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:16px;outline:none}.scroller:focus-visible{box-shadow:inset 0 0 0 2px var(--nha-focus)}cdk-virtual-scroll-viewport.scroller{height:100%}.row+.row{margin-top:20px}.message{display:flex;flex-direction:column;align-items:flex-start;gap:6px;min-width:0}.message[data-role=user]{align-items:flex-end}.author{margin:0;color:var(--nha-text-muted);font-size:11px;font-weight:600}.bubble{max-width:100%;border-radius:var(--nha-radius-control);padding:8px 12px;font-size:14px;line-height:1.5;overflow-wrap:anywhere}.plain{white-space:pre-wrap}.message[data-role=user] .bubble{max-width:88%;color:var(--nha-user-text);background:var(--nha-user-bubble)}.message[data-others=true]{align-items:flex-start}.message[data-others=true] .bubble{color:var(--nha-text);background:var(--nha-surface-strong)}.message[data-author=trigger]{align-items:stretch}.message[data-author=trigger] .bubble{max-width:none;color:var(--nha-text-muted);background:transparent;border:1px dashed var(--nha-border)}.message[data-role=assistant] .bubble,.message[data-role=tool] .bubble{padding-inline:0;background:transparent}.part{width:100%}.files{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px;max-width:88%;min-width:0;margin:0;padding:0;list-style:none}.message:not([data-role=user]) .files,.message[data-others=true] .files{justify-content:flex-start}.file{display:flex;align-items:center;gap:6px;min-width:0;max-width:100%;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:4px 10px;color:var(--nha-text);background:var(--nha-surface);font-size:12px}.file-icon{flex-shrink:0;color:var(--nha-text-muted)}.file-name{min-width:0;overflow:hidden;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.file-meta{flex-shrink:0;color:var(--nha-text-muted);white-space:nowrap}:host ::ng-deep .markdown :first-child{margin-top:0}:host ::ng-deep .markdown :last-child{margin-bottom:0}:host ::ng-deep .markdown p,:host ::ng-deep .markdown ul,:host ::ng-deep .markdown ol,:host ::ng-deep .markdown pre,:host ::ng-deep .markdown blockquote,:host ::ng-deep .markdown table{margin:0 0 8px}:host ::ng-deep .markdown ul,:host ::ng-deep .markdown ol{padding-left:20px}:host ::ng-deep .markdown h1,:host ::ng-deep .markdown h2,:host ::ng-deep .markdown h3,:host ::ng-deep .markdown h4,:host ::ng-deep .markdown h5,:host ::ng-deep .markdown h6{margin:10px 0 6px;font-size:15px;line-height:1.3}:host ::ng-deep .markdown hr{margin:12px 0;border:0;border-top:1px solid var(--nha-border)}:host ::ng-deep .markdown a{color:var(--nha-highlight);text-decoration:underline}:host ::ng-deep .markdown a:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host ::ng-deep .markdown code{border-radius:6px;padding:1px 4px;background:var(--nha-surface-strong);font-family:var(--nha-mono);font-size:12.5px}:host ::ng-deep .markdown pre{overflow-x:auto;border-radius:8px;padding:8px;background:var(--nha-surface-strong)}:host ::ng-deep .markdown pre code{padding:0;background:none}:host ::ng-deep .markdown blockquote{border-left:3px solid var(--nha-border);padding-left:10px;color:var(--nha-text-muted)}:host ::ng-deep .markdown table{display:block;max-width:100%;overflow-x:auto;border-collapse:collapse;font-size:13px}:host ::ng-deep .markdown th,:host ::ng-deep .markdown td{border:1px solid var(--nha-border);padding:4px 8px;text-align:left}.working{display:flex;align-items:center;gap:8px;margin:14px 0 0;color:var(--nha-text-muted);font-size:13px}.elapsed{font-variant-numeric:tabular-nums}.dots{display:inline-flex;gap:3px}.dots span{width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.6}@media(prefers-reduced-motion:no-preference){.dots span{animation:nha-bounce 1s ease-in-out infinite}.dots span:nth-child(2){animation-delay:.15s}.dots span:nth-child(3){animation-delay:.3s}}@keyframes nha-bounce{0%,80%,to{transform:translateY(0);opacity:.35}40%{transform:translateY(-3px);opacity:1}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i1.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i1.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "component", type: NhAssistantToolCallGroupComponent, selector: "nh-assistant-tool-call-group", inputs: ["calls"] }, { kind: "component", type: NhAssistantApprovalCardComponent, selector: "nh-assistant-approval-card", inputs: ["approval", "deciding", "canDecide", "decisionOwner", "waitingFor", "canGiveFeedback"], outputs: ["decide"] }, { kind: "component", type: NhAssistantConnectionRequiredComponent, selector: "nh-assistant-connection-required", inputs: ["part", "canRetry"], outputs: ["retry"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantMarkdownPipe, name: "nhAssistantMarkdown" }, { kind: "pipe", type: NhAssistantPartGroupsPipe, name: "nhAssistantPartGroups" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11924
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", type: NhAssistantThreadComponent, isStandalone: true, selector: "nh-assistant-thread", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, streaming: { classPropertyName: "streaming", publicName: "streaming", isSignal: true, isRequired: false, transformFunction: null }, deciding: { classPropertyName: "deciding", publicName: "deciding", isSignal: true, isRequired: false, transformFunction: null }, markdown: { classPropertyName: "markdown", publicName: "markdown", isSignal: true, isRequired: false, transformFunction: null }, members: { classPropertyName: "members", publicName: "members", isSignal: true, isRequired: false, transformFunction: null }, currentActorId: { classPropertyName: "currentActorId", publicName: "currentActorId", isSignal: true, isRequired: false, transformFunction: null }, canDecide: { classPropertyName: "canDecide", publicName: "canDecide", isSignal: true, isRequired: false, transformFunction: null }, decisionOwner: { classPropertyName: "decisionOwner", publicName: "decisionOwner", isSignal: true, isRequired: false, transformFunction: null }, approvalRights: { classPropertyName: "approvalRights", publicName: "approvalRights", isSignal: true, isRequired: false, transformFunction: null }, writingMessageId: { classPropertyName: "writingMessageId", publicName: "writingMessageId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { approvalDecision: "approvalDecision", approvalChoice: "approvalChoice", retryMessage: "retryMessage" }, viewQueries: [{ propertyName: "scroller", first: true, predicate: ["scroller"], descendants: true, isSignal: true }, { propertyName: "viewport", first: true, predicate: CdkVirtualScrollViewport, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"log\" role=\"log\" aria-live=\"polite\" [attr.aria-label]=\"'nh-assistant.thread.label' | translate\" [attr.aria-busy]=\"streaming()\">\n @if (virtual()) {\n <cdk-virtual-scroll-viewport class=\"scroller\" itemSize=\"120\" minBufferPx=\"1200\" maxBufferPx=\"2400\">\n <div class=\"row\" *cdkVirtualFor=\"let message of messages(); trackBy: trackMessage\">\n <ng-container [ngTemplateOutlet]=\"messageTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message }\" />\n @if (working() && message.id === workingAfterId()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </div>\n @if (working() && workingAfterId() === null) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </cdk-virtual-scroll-viewport>\n } @else {\n <div class=\"scroller\" #scroller tabindex=\"0\">\n @for (message of messages(); track message.id) {\n <div class=\"row\">\n <ng-container [ngTemplateOutlet]=\"messageTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message }\" />\n <!-- The answer a turn still writes (writingMessageId) keeps the indicator under it, also when messages arrived after it. -->\n @if (working() && message.id === workingAfterId()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </div>\n }\n @if (working() && workingAfterId() === null) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </div>\n }\n</div>\n\n<ng-template #messageTemplate let-message>\n <article\n class=\"message\"\n [attr.data-role]=\"message.role\"\n [attr.data-others]=\"isOthers(message) ? 'true' : null\"\n [attr.data-author]=\"isTrigger(message) ? 'trigger' : isRule(message) ? 'rule' : isApplication(message) ? 'application' : null\"\n [attr.aria-label]=\"isApplication(message) ? ('nh-assistant.thread.application-label' | translate) : null\">\n @if (isTrigger(message)) {\n <p class=\"author\">{{ 'nh-assistant.thread.trigger' | translate }}</p>\n } @else if (isRule(message)) {\n <p class=\"author\">{{ 'nh-assistant.thread.rule' | translate }}</p>\n } @else if (isApplication(message)) {\n <!-- The application's message reads as the application's, also when it names who in the application posted it. -->\n <p class=\"author\">\n <nh-assistant-icon class=\"author-icon\" name=\"bell\" />\n @if (message.application?.senderName; as sender) {\n <!-- The space keeps the sender and the tag apart for screen readers; the flex gap shows it. -->\n <span>{{ sender }}</span>{{ ' ' }}<span class=\"tag\">{{ 'nh-assistant.thread.application' | translate }}</span>\n } @else {\n <span>{{ 'nh-assistant.thread.application' | translate }}</span>\n }\n @if (message.application?.trusted) {\n <!-- People see which of the application's messages the assistant follows as an instruction. -->\n {{ ' ' }}<span class=\"tag trusted\">{{ 'nh-assistant.thread.application-trusted' | translate }}</span>\n }\n </p>\n @if (message.application?.title; as title) {\n <p class=\"application-title\">{{ title }}</p>\n }\n } @else if (isOthers(message)) {\n <p class=\"author\">{{ authorName(message) ?? ('nh-assistant.thread.participant' | translate) }}</p>\n } @else {\n <p class=\"author\">{{ 'nh-assistant.thread.' + (message.role === 'user' ? 'you' : message.role) | translate }}</p>\n }\n @for (group of message.parts | nhAssistantPartGroups; track group.key) {\n @switch (group.kind) {\n @case ('text') {\n @if ((message.role === 'assistant' || isApplication(message)) && markdown()) {\n <div class=\"bubble markdown\" [innerHTML]=\"group.part.text | nhAssistantMarkdown\"></div>\n } @else {\n <div class=\"bubble plain\">{{ group.part.text }}</div>\n }\n }\n @case ('tools') {\n <nh-assistant-tool-call-group class=\"part\" [calls]=\"group.calls\" />\n }\n @case ('files') {\n <ul class=\"files\" [attr.aria-label]=\"'nh-assistant.thread.files' | translate\">\n @for (file of group.files; track $index) {\n <li class=\"file\">\n <nh-assistant-icon class=\"file-icon\" name=\"file\" />\n <span class=\"file-name\" [attr.title]=\"file.name\">{{ file.name }}</span>\n <span class=\"file-meta\">{{ 'nh-assistant.thread.file-meta' | translate: fileMeta(file) }}</span>\n </li>\n }\n </ul>\n }\n @case ('connection') {\n <nh-assistant-connection-required\n class=\"part\"\n [part]=\"group.part\"\n [canRetry]=\"!streaming()\"\n (retry)=\"retryAfterConnect(message)\" />\n }\n @case ('approval') {\n <nh-assistant-approval-card\n class=\"part\"\n [approval]=\"group.part\"\n [deciding]=\"deciding()\"\n [canDecide]=\"rightFor(group.part).canDecide\"\n [decisionOwner]=\"rightFor(group.part).decisionOwner\"\n [waitingFor]=\"rightFor(group.part).waitingFor\"\n [canGiveFeedback]=\"rightFor(group.part).canGiveFeedback === true\"\n (decide)=\"choose(group.part, $event)\" />\n }\n }\n }\n @if (isApplication(message) && message.application?.link; as link) {\n <nh-assistant-message-link\n class=\"application-link\"\n [url]=\"link.url\"\n [label]=\"link.label || ('nh-assistant.thread.application-link' | translate)\" />\n }\n </article>\n</ng-template>\n\n<ng-template #workingTemplate>\n <p class=\"working\" role=\"status\">\n <span class=\"dots\" aria-hidden=\"true\"><span></span><span></span><span></span></span>\n {{ 'nh-assistant.thread.working' | translate }}\n @if (elapsedSeconds() > 0) {\n <span class=\"elapsed\">{{ 'nh-assistant.thread.elapsed' | translate: { seconds: elapsedSeconds() } }}</span>\n }\n </p>\n</ng-template>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:flex;flex-direction:column;min-height:0;color:var(--nha-text)}.log{display:flex;flex:1;flex-direction:column;min-height:0}.scroller{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:16px;outline:none}.scroller:focus-visible{box-shadow:inset 0 0 0 2px var(--nha-focus)}cdk-virtual-scroll-viewport.scroller{height:100%}.row+.row{margin-top:20px}.message{display:flex;flex-direction:column;align-items:flex-start;gap:6px;min-width:0}.message[data-role=user]{align-items:flex-end}.author{margin:0;color:var(--nha-text-muted);font-size:11px;font-weight:600}.bubble{max-width:100%;border-radius:var(--nha-radius-control);padding:8px 12px;font-size:14px;line-height:1.5;overflow-wrap:anywhere}.plain{white-space:pre-wrap}.message[data-role=user] .bubble{max-width:88%;color:var(--nha-user-text);background:var(--nha-user-bubble)}.message[data-others=true]{align-items:flex-start}.message[data-others=true] .bubble{color:var(--nha-text);background:var(--nha-surface-strong)}.message[data-author=trigger]{align-items:stretch}.message[data-author=trigger] .bubble{max-width:none;color:var(--nha-text-muted);background:transparent;border:1px dashed var(--nha-border)}.message[data-role=assistant] .bubble,.message[data-role=tool] .bubble{padding-inline:0;background:transparent}.part{width:100%}.files{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px;max-width:88%;min-width:0;margin:0;padding:0;list-style:none}.message:not([data-role=user]) .files,.message[data-others=true] .files{justify-content:flex-start}.file{display:flex;align-items:center;gap:6px;min-width:0;max-width:100%;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:4px 10px;color:var(--nha-text);background:var(--nha-surface);font-size:12px}.file-icon{flex-shrink:0;color:var(--nha-text-muted)}.file-name{min-width:0;overflow:hidden;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.file-meta{flex-shrink:0;color:var(--nha-text-muted);white-space:nowrap}:host ::ng-deep .markdown :first-child{margin-top:0}:host ::ng-deep .markdown :last-child{margin-bottom:0}:host ::ng-deep .markdown p,:host ::ng-deep .markdown ul,:host ::ng-deep .markdown ol,:host ::ng-deep .markdown pre,:host ::ng-deep .markdown blockquote,:host ::ng-deep .markdown table{margin:0 0 8px}:host ::ng-deep .markdown ul,:host ::ng-deep .markdown ol{padding-left:20px}:host ::ng-deep .markdown h1,:host ::ng-deep .markdown h2,:host ::ng-deep .markdown h3,:host ::ng-deep .markdown h4,:host ::ng-deep .markdown h5,:host ::ng-deep .markdown h6{margin:10px 0 6px;font-size:15px;line-height:1.3}:host ::ng-deep .markdown hr{margin:12px 0;border:0;border-top:1px solid var(--nha-border)}:host ::ng-deep .markdown a{color:var(--nha-highlight);text-decoration:underline}:host ::ng-deep .markdown a:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host ::ng-deep .markdown code{border-radius:6px;padding:1px 4px;background:var(--nha-surface-strong);font-family:var(--nha-mono);font-size:12.5px}:host ::ng-deep .markdown pre{overflow-x:auto;border-radius:8px;padding:8px;background:var(--nha-surface-strong)}:host ::ng-deep .markdown pre code{padding:0;background:none}:host ::ng-deep .markdown blockquote{border-left:3px solid var(--nha-border);padding-left:10px;color:var(--nha-text-muted)}:host ::ng-deep .markdown table{display:block;max-width:100%;overflow-x:auto;border-collapse:collapse;font-size:13px}:host ::ng-deep .markdown th,:host ::ng-deep .markdown td{border:1px solid var(--nha-border);padding:4px 8px;text-align:left}.working{display:flex;align-items:center;gap:8px;margin:14px 0 0;color:var(--nha-text-muted);font-size:13px}.elapsed{font-variant-numeric:tabular-nums}.dots{display:inline-flex;gap:3px}.dots span{width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.6}@media(prefers-reduced-motion:no-preference){.dots span{animation:nha-bounce 1s ease-in-out infinite}.dots span:nth-child(2){animation-delay:.15s}.dots span:nth-child(3){animation-delay:.3s}}@keyframes nha-bounce{0%,80%,to{transform:translateY(0);opacity:.35}40%{transform:translateY(-3px);opacity:1}}.message[data-author=application]{align-items:stretch;border-left:3px solid var(--nha-accent);border-radius:var(--nha-radius-control);padding:8px 12px;background:var(--nha-surface-strong)}.message[data-author=application] .author{display:flex;align-items:center;gap:6px}.message[data-author=application] .author-icon{color:var(--nha-accent)}.message[data-author=application] .tag{border:1px solid var(--nha-border);border-radius:999px;padding:0 6px;color:var(--nha-text-muted);font-weight:500}.message[data-author=application] .application-title{margin:0;color:var(--nha-text);font-size:14px;font-weight:600}.message[data-author=application] .bubble{max-width:none;padding:0;color:var(--nha-text);background:transparent}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i1.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i1.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "component", type: NhAssistantMessageLinkComponent, selector: "nh-assistant-message-link", inputs: ["url", "label"] }, { kind: "component", type: NhAssistantToolCallGroupComponent, selector: "nh-assistant-tool-call-group", inputs: ["calls"] }, { kind: "component", type: NhAssistantApprovalCardComponent, selector: "nh-assistant-approval-card", inputs: ["approval", "deciding", "canDecide", "decisionOwner", "waitingFor", "canGiveFeedback"], outputs: ["decide"] }, { kind: "component", type: NhAssistantConnectionRequiredComponent, selector: "nh-assistant-connection-required", inputs: ["part", "canRetry"], outputs: ["retry"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantMarkdownPipe, name: "nhAssistantMarkdown" }, { kind: "pipe", type: NhAssistantPartGroupsPipe, name: "nhAssistantPartGroups" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10943
11925
  }
10944
11926
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantThreadComponent, decorators: [{
10945
11927
  type: Component,
@@ -10949,12 +11931,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
10949
11931
  TranslatePipe,
10950
11932
  NhAssistantIconComponent,
10951
11933
  NhAssistantMarkdownPipe,
11934
+ NhAssistantMessageLinkComponent,
10952
11935
  NhAssistantPartGroupsPipe,
10953
11936
  NhAssistantToolCallGroupComponent,
10954
11937
  NhAssistantApprovalCardComponent,
10955
11938
  NhAssistantConnectionRequiredComponent
10956
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"log\" role=\"log\" aria-live=\"polite\" [attr.aria-label]=\"'nh-assistant.thread.label' | translate\" [attr.aria-busy]=\"streaming()\">\n @if (virtual()) {\n <cdk-virtual-scroll-viewport class=\"scroller\" itemSize=\"120\" minBufferPx=\"1200\" maxBufferPx=\"2400\">\n <div class=\"row\" *cdkVirtualFor=\"let message of messages(); trackBy: trackMessage\">\n <ng-container [ngTemplateOutlet]=\"messageTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message }\" />\n </div>\n @if (working()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </cdk-virtual-scroll-viewport>\n } @else {\n <div class=\"scroller\" #scroller tabindex=\"0\">\n @for (message of messages(); track message.id) {\n <div class=\"row\">\n <ng-container [ngTemplateOutlet]=\"messageTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message }\" />\n </div>\n }\n @if (working()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </div>\n }\n</div>\n\n<ng-template #messageTemplate let-message>\n <article\n class=\"message\"\n [attr.data-role]=\"message.role\"\n [attr.data-others]=\"isOthers(message) ? 'true' : null\"\n [attr.data-author]=\"isTrigger(message) ? 'trigger' : isRule(message) ? 'rule' : null\">\n @if (isTrigger(message)) {\n <p class=\"author\">{{ 'nh-assistant.thread.trigger' | translate }}</p>\n } @else if (isRule(message)) {\n <p class=\"author\">{{ 'nh-assistant.thread.rule' | translate }}</p>\n } @else if (isOthers(message)) {\n <p class=\"author\">{{ authorName(message) ?? ('nh-assistant.thread.participant' | translate) }}</p>\n } @else {\n <p class=\"author\">{{ 'nh-assistant.thread.' + (message.role === 'user' ? 'you' : message.role) | translate }}</p>\n }\n @for (group of message.parts | nhAssistantPartGroups; track group.key) {\n @switch (group.kind) {\n @case ('text') {\n @if (message.role === 'assistant' && markdown()) {\n <div class=\"bubble markdown\" [innerHTML]=\"group.part.text | nhAssistantMarkdown\"></div>\n } @else {\n <div class=\"bubble plain\">{{ group.part.text }}</div>\n }\n }\n @case ('tools') {\n <nh-assistant-tool-call-group class=\"part\" [calls]=\"group.calls\" />\n }\n @case ('files') {\n <ul class=\"files\" [attr.aria-label]=\"'nh-assistant.thread.files' | translate\">\n @for (file of group.files; track $index) {\n <li class=\"file\">\n <nh-assistant-icon class=\"file-icon\" name=\"file\" />\n <span class=\"file-name\" [attr.title]=\"file.name\">{{ file.name }}</span>\n <span class=\"file-meta\">{{ 'nh-assistant.thread.file-meta' | translate: fileMeta(file) }}</span>\n </li>\n }\n </ul>\n }\n @case ('connection') {\n <nh-assistant-connection-required\n class=\"part\"\n [part]=\"group.part\"\n [canRetry]=\"!streaming()\"\n (retry)=\"retryAfterConnect(message)\" />\n }\n @case ('approval') {\n <nh-assistant-approval-card\n class=\"part\"\n [approval]=\"group.part\"\n [deciding]=\"deciding()\"\n [canDecide]=\"rightFor(group.part).canDecide\"\n [decisionOwner]=\"rightFor(group.part).decisionOwner\"\n [waitingFor]=\"rightFor(group.part).waitingFor\"\n [canGiveFeedback]=\"rightFor(group.part).canGiveFeedback === true\"\n (decide)=\"choose(group.part, $event)\" />\n }\n }\n }\n </article>\n</ng-template>\n\n<ng-template #workingTemplate>\n <p class=\"working\" role=\"status\">\n <span class=\"dots\" aria-hidden=\"true\"><span></span><span></span><span></span></span>\n {{ 'nh-assistant.thread.working' | translate }}\n @if (elapsedSeconds() > 0) {\n <span class=\"elapsed\">{{ 'nh-assistant.thread.elapsed' | translate: { seconds: elapsedSeconds() } }}</span>\n }\n </p>\n</ng-template>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:flex;flex-direction:column;min-height:0;color:var(--nha-text)}.log{display:flex;flex:1;flex-direction:column;min-height:0}.scroller{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:16px;outline:none}.scroller:focus-visible{box-shadow:inset 0 0 0 2px var(--nha-focus)}cdk-virtual-scroll-viewport.scroller{height:100%}.row+.row{margin-top:20px}.message{display:flex;flex-direction:column;align-items:flex-start;gap:6px;min-width:0}.message[data-role=user]{align-items:flex-end}.author{margin:0;color:var(--nha-text-muted);font-size:11px;font-weight:600}.bubble{max-width:100%;border-radius:var(--nha-radius-control);padding:8px 12px;font-size:14px;line-height:1.5;overflow-wrap:anywhere}.plain{white-space:pre-wrap}.message[data-role=user] .bubble{max-width:88%;color:var(--nha-user-text);background:var(--nha-user-bubble)}.message[data-others=true]{align-items:flex-start}.message[data-others=true] .bubble{color:var(--nha-text);background:var(--nha-surface-strong)}.message[data-author=trigger]{align-items:stretch}.message[data-author=trigger] .bubble{max-width:none;color:var(--nha-text-muted);background:transparent;border:1px dashed var(--nha-border)}.message[data-role=assistant] .bubble,.message[data-role=tool] .bubble{padding-inline:0;background:transparent}.part{width:100%}.files{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px;max-width:88%;min-width:0;margin:0;padding:0;list-style:none}.message:not([data-role=user]) .files,.message[data-others=true] .files{justify-content:flex-start}.file{display:flex;align-items:center;gap:6px;min-width:0;max-width:100%;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:4px 10px;color:var(--nha-text);background:var(--nha-surface);font-size:12px}.file-icon{flex-shrink:0;color:var(--nha-text-muted)}.file-name{min-width:0;overflow:hidden;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.file-meta{flex-shrink:0;color:var(--nha-text-muted);white-space:nowrap}:host ::ng-deep .markdown :first-child{margin-top:0}:host ::ng-deep .markdown :last-child{margin-bottom:0}:host ::ng-deep .markdown p,:host ::ng-deep .markdown ul,:host ::ng-deep .markdown ol,:host ::ng-deep .markdown pre,:host ::ng-deep .markdown blockquote,:host ::ng-deep .markdown table{margin:0 0 8px}:host ::ng-deep .markdown ul,:host ::ng-deep .markdown ol{padding-left:20px}:host ::ng-deep .markdown h1,:host ::ng-deep .markdown h2,:host ::ng-deep .markdown h3,:host ::ng-deep .markdown h4,:host ::ng-deep .markdown h5,:host ::ng-deep .markdown h6{margin:10px 0 6px;font-size:15px;line-height:1.3}:host ::ng-deep .markdown hr{margin:12px 0;border:0;border-top:1px solid var(--nha-border)}:host ::ng-deep .markdown a{color:var(--nha-highlight);text-decoration:underline}:host ::ng-deep .markdown a:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host ::ng-deep .markdown code{border-radius:6px;padding:1px 4px;background:var(--nha-surface-strong);font-family:var(--nha-mono);font-size:12.5px}:host ::ng-deep .markdown pre{overflow-x:auto;border-radius:8px;padding:8px;background:var(--nha-surface-strong)}:host ::ng-deep .markdown pre code{padding:0;background:none}:host ::ng-deep .markdown blockquote{border-left:3px solid var(--nha-border);padding-left:10px;color:var(--nha-text-muted)}:host ::ng-deep .markdown table{display:block;max-width:100%;overflow-x:auto;border-collapse:collapse;font-size:13px}:host ::ng-deep .markdown th,:host ::ng-deep .markdown td{border:1px solid var(--nha-border);padding:4px 8px;text-align:left}.working{display:flex;align-items:center;gap:8px;margin:14px 0 0;color:var(--nha-text-muted);font-size:13px}.elapsed{font-variant-numeric:tabular-nums}.dots{display:inline-flex;gap:3px}.dots span{width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.6}@media(prefers-reduced-motion:no-preference){.dots span{animation:nha-bounce 1s ease-in-out infinite}.dots span:nth-child(2){animation-delay:.15s}.dots span:nth-child(3){animation-delay:.3s}}@keyframes nha-bounce{0%,80%,to{transform:translateY(0);opacity:.35}40%{transform:translateY(-3px);opacity:1}}\n"] }]
10957
- }], ctorParameters: () => [], propDecorators: { messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: true }] }], streaming: [{ type: i0.Input, args: [{ isSignal: true, alias: "streaming", required: false }] }], deciding: [{ type: i0.Input, args: [{ isSignal: true, alias: "deciding", required: false }] }], markdown: [{ type: i0.Input, args: [{ isSignal: true, alias: "markdown", required: false }] }], members: [{ type: i0.Input, args: [{ isSignal: true, alias: "members", required: false }] }], currentActorId: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentActorId", required: false }] }], canDecide: [{ type: i0.Input, args: [{ isSignal: true, alias: "canDecide", required: false }] }], decisionOwner: [{ type: i0.Input, args: [{ isSignal: true, alias: "decisionOwner", required: false }] }], approvalRights: [{ type: i0.Input, args: [{ isSignal: true, alias: "approvalRights", required: false }] }], approvalDecision: [{ type: i0.Output, args: ["approvalDecision"] }], approvalChoice: [{ type: i0.Output, args: ["approvalChoice"] }], retryMessage: [{ type: i0.Output, args: ["retryMessage"] }], scroller: [{ type: i0.ViewChild, args: ['scroller', { isSignal: true }] }], viewport: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkVirtualScrollViewport), { isSignal: true }] }] } });
11939
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"log\" role=\"log\" aria-live=\"polite\" [attr.aria-label]=\"'nh-assistant.thread.label' | translate\" [attr.aria-busy]=\"streaming()\">\n @if (virtual()) {\n <cdk-virtual-scroll-viewport class=\"scroller\" itemSize=\"120\" minBufferPx=\"1200\" maxBufferPx=\"2400\">\n <div class=\"row\" *cdkVirtualFor=\"let message of messages(); trackBy: trackMessage\">\n <ng-container [ngTemplateOutlet]=\"messageTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message }\" />\n @if (working() && message.id === workingAfterId()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </div>\n @if (working() && workingAfterId() === null) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </cdk-virtual-scroll-viewport>\n } @else {\n <div class=\"scroller\" #scroller tabindex=\"0\">\n @for (message of messages(); track message.id) {\n <div class=\"row\">\n <ng-container [ngTemplateOutlet]=\"messageTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message }\" />\n <!-- The answer a turn still writes (writingMessageId) keeps the indicator under it, also when messages arrived after it. -->\n @if (working() && message.id === workingAfterId()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </div>\n }\n @if (working() && workingAfterId() === null) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </div>\n }\n</div>\n\n<ng-template #messageTemplate let-message>\n <article\n class=\"message\"\n [attr.data-role]=\"message.role\"\n [attr.data-others]=\"isOthers(message) ? 'true' : null\"\n [attr.data-author]=\"isTrigger(message) ? 'trigger' : isRule(message) ? 'rule' : isApplication(message) ? 'application' : null\"\n [attr.aria-label]=\"isApplication(message) ? ('nh-assistant.thread.application-label' | translate) : null\">\n @if (isTrigger(message)) {\n <p class=\"author\">{{ 'nh-assistant.thread.trigger' | translate }}</p>\n } @else if (isRule(message)) {\n <p class=\"author\">{{ 'nh-assistant.thread.rule' | translate }}</p>\n } @else if (isApplication(message)) {\n <!-- The application's message reads as the application's, also when it names who in the application posted it. -->\n <p class=\"author\">\n <nh-assistant-icon class=\"author-icon\" name=\"bell\" />\n @if (message.application?.senderName; as sender) {\n <!-- The space keeps the sender and the tag apart for screen readers; the flex gap shows it. -->\n <span>{{ sender }}</span>{{ ' ' }}<span class=\"tag\">{{ 'nh-assistant.thread.application' | translate }}</span>\n } @else {\n <span>{{ 'nh-assistant.thread.application' | translate }}</span>\n }\n @if (message.application?.trusted) {\n <!-- People see which of the application's messages the assistant follows as an instruction. -->\n {{ ' ' }}<span class=\"tag trusted\">{{ 'nh-assistant.thread.application-trusted' | translate }}</span>\n }\n </p>\n @if (message.application?.title; as title) {\n <p class=\"application-title\">{{ title }}</p>\n }\n } @else if (isOthers(message)) {\n <p class=\"author\">{{ authorName(message) ?? ('nh-assistant.thread.participant' | translate) }}</p>\n } @else {\n <p class=\"author\">{{ 'nh-assistant.thread.' + (message.role === 'user' ? 'you' : message.role) | translate }}</p>\n }\n @for (group of message.parts | nhAssistantPartGroups; track group.key) {\n @switch (group.kind) {\n @case ('text') {\n @if ((message.role === 'assistant' || isApplication(message)) && markdown()) {\n <div class=\"bubble markdown\" [innerHTML]=\"group.part.text | nhAssistantMarkdown\"></div>\n } @else {\n <div class=\"bubble plain\">{{ group.part.text }}</div>\n }\n }\n @case ('tools') {\n <nh-assistant-tool-call-group class=\"part\" [calls]=\"group.calls\" />\n }\n @case ('files') {\n <ul class=\"files\" [attr.aria-label]=\"'nh-assistant.thread.files' | translate\">\n @for (file of group.files; track $index) {\n <li class=\"file\">\n <nh-assistant-icon class=\"file-icon\" name=\"file\" />\n <span class=\"file-name\" [attr.title]=\"file.name\">{{ file.name }}</span>\n <span class=\"file-meta\">{{ 'nh-assistant.thread.file-meta' | translate: fileMeta(file) }}</span>\n </li>\n }\n </ul>\n }\n @case ('connection') {\n <nh-assistant-connection-required\n class=\"part\"\n [part]=\"group.part\"\n [canRetry]=\"!streaming()\"\n (retry)=\"retryAfterConnect(message)\" />\n }\n @case ('approval') {\n <nh-assistant-approval-card\n class=\"part\"\n [approval]=\"group.part\"\n [deciding]=\"deciding()\"\n [canDecide]=\"rightFor(group.part).canDecide\"\n [decisionOwner]=\"rightFor(group.part).decisionOwner\"\n [waitingFor]=\"rightFor(group.part).waitingFor\"\n [canGiveFeedback]=\"rightFor(group.part).canGiveFeedback === true\"\n (decide)=\"choose(group.part, $event)\" />\n }\n }\n }\n @if (isApplication(message) && message.application?.link; as link) {\n <nh-assistant-message-link\n class=\"application-link\"\n [url]=\"link.url\"\n [label]=\"link.label || ('nh-assistant.thread.application-link' | translate)\" />\n }\n </article>\n</ng-template>\n\n<ng-template #workingTemplate>\n <p class=\"working\" role=\"status\">\n <span class=\"dots\" aria-hidden=\"true\"><span></span><span></span><span></span></span>\n {{ 'nh-assistant.thread.working' | translate }}\n @if (elapsedSeconds() > 0) {\n <span class=\"elapsed\">{{ 'nh-assistant.thread.elapsed' | translate: { seconds: elapsedSeconds() } }}</span>\n }\n </p>\n</ng-template>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:flex;flex-direction:column;min-height:0;color:var(--nha-text)}.log{display:flex;flex:1;flex-direction:column;min-height:0}.scroller{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:16px;outline:none}.scroller:focus-visible{box-shadow:inset 0 0 0 2px var(--nha-focus)}cdk-virtual-scroll-viewport.scroller{height:100%}.row+.row{margin-top:20px}.message{display:flex;flex-direction:column;align-items:flex-start;gap:6px;min-width:0}.message[data-role=user]{align-items:flex-end}.author{margin:0;color:var(--nha-text-muted);font-size:11px;font-weight:600}.bubble{max-width:100%;border-radius:var(--nha-radius-control);padding:8px 12px;font-size:14px;line-height:1.5;overflow-wrap:anywhere}.plain{white-space:pre-wrap}.message[data-role=user] .bubble{max-width:88%;color:var(--nha-user-text);background:var(--nha-user-bubble)}.message[data-others=true]{align-items:flex-start}.message[data-others=true] .bubble{color:var(--nha-text);background:var(--nha-surface-strong)}.message[data-author=trigger]{align-items:stretch}.message[data-author=trigger] .bubble{max-width:none;color:var(--nha-text-muted);background:transparent;border:1px dashed var(--nha-border)}.message[data-role=assistant] .bubble,.message[data-role=tool] .bubble{padding-inline:0;background:transparent}.part{width:100%}.files{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px;max-width:88%;min-width:0;margin:0;padding:0;list-style:none}.message:not([data-role=user]) .files,.message[data-others=true] .files{justify-content:flex-start}.file{display:flex;align-items:center;gap:6px;min-width:0;max-width:100%;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:4px 10px;color:var(--nha-text);background:var(--nha-surface);font-size:12px}.file-icon{flex-shrink:0;color:var(--nha-text-muted)}.file-name{min-width:0;overflow:hidden;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.file-meta{flex-shrink:0;color:var(--nha-text-muted);white-space:nowrap}:host ::ng-deep .markdown :first-child{margin-top:0}:host ::ng-deep .markdown :last-child{margin-bottom:0}:host ::ng-deep .markdown p,:host ::ng-deep .markdown ul,:host ::ng-deep .markdown ol,:host ::ng-deep .markdown pre,:host ::ng-deep .markdown blockquote,:host ::ng-deep .markdown table{margin:0 0 8px}:host ::ng-deep .markdown ul,:host ::ng-deep .markdown ol{padding-left:20px}:host ::ng-deep .markdown h1,:host ::ng-deep .markdown h2,:host ::ng-deep .markdown h3,:host ::ng-deep .markdown h4,:host ::ng-deep .markdown h5,:host ::ng-deep .markdown h6{margin:10px 0 6px;font-size:15px;line-height:1.3}:host ::ng-deep .markdown hr{margin:12px 0;border:0;border-top:1px solid var(--nha-border)}:host ::ng-deep .markdown a{color:var(--nha-highlight);text-decoration:underline}:host ::ng-deep .markdown a:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host ::ng-deep .markdown code{border-radius:6px;padding:1px 4px;background:var(--nha-surface-strong);font-family:var(--nha-mono);font-size:12.5px}:host ::ng-deep .markdown pre{overflow-x:auto;border-radius:8px;padding:8px;background:var(--nha-surface-strong)}:host ::ng-deep .markdown pre code{padding:0;background:none}:host ::ng-deep .markdown blockquote{border-left:3px solid var(--nha-border);padding-left:10px;color:var(--nha-text-muted)}:host ::ng-deep .markdown table{display:block;max-width:100%;overflow-x:auto;border-collapse:collapse;font-size:13px}:host ::ng-deep .markdown th,:host ::ng-deep .markdown td{border:1px solid var(--nha-border);padding:4px 8px;text-align:left}.working{display:flex;align-items:center;gap:8px;margin:14px 0 0;color:var(--nha-text-muted);font-size:13px}.elapsed{font-variant-numeric:tabular-nums}.dots{display:inline-flex;gap:3px}.dots span{width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.6}@media(prefers-reduced-motion:no-preference){.dots span{animation:nha-bounce 1s ease-in-out infinite}.dots span:nth-child(2){animation-delay:.15s}.dots span:nth-child(3){animation-delay:.3s}}@keyframes nha-bounce{0%,80%,to{transform:translateY(0);opacity:.35}40%{transform:translateY(-3px);opacity:1}}.message[data-author=application]{align-items:stretch;border-left:3px solid var(--nha-accent);border-radius:var(--nha-radius-control);padding:8px 12px;background:var(--nha-surface-strong)}.message[data-author=application] .author{display:flex;align-items:center;gap:6px}.message[data-author=application] .author-icon{color:var(--nha-accent)}.message[data-author=application] .tag{border:1px solid var(--nha-border);border-radius:999px;padding:0 6px;color:var(--nha-text-muted);font-weight:500}.message[data-author=application] .application-title{margin:0;color:var(--nha-text);font-size:14px;font-weight:600}.message[data-author=application] .bubble{max-width:none;padding:0;color:var(--nha-text);background:transparent}\n"] }]
11940
+ }], ctorParameters: () => [], propDecorators: { messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: true }] }], streaming: [{ type: i0.Input, args: [{ isSignal: true, alias: "streaming", required: false }] }], deciding: [{ type: i0.Input, args: [{ isSignal: true, alias: "deciding", required: false }] }], markdown: [{ type: i0.Input, args: [{ isSignal: true, alias: "markdown", required: false }] }], members: [{ type: i0.Input, args: [{ isSignal: true, alias: "members", required: false }] }], currentActorId: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentActorId", required: false }] }], canDecide: [{ type: i0.Input, args: [{ isSignal: true, alias: "canDecide", required: false }] }], decisionOwner: [{ type: i0.Input, args: [{ isSignal: true, alias: "decisionOwner", required: false }] }], approvalRights: [{ type: i0.Input, args: [{ isSignal: true, alias: "approvalRights", required: false }] }], writingMessageId: [{ type: i0.Input, args: [{ isSignal: true, alias: "writingMessageId", required: false }] }], approvalDecision: [{ type: i0.Output, args: ["approvalDecision"] }], approvalChoice: [{ type: i0.Output, args: ["approvalChoice"] }], retryMessage: [{ type: i0.Output, args: ["retryMessage"] }], scroller: [{ type: i0.ViewChild, args: ['scroller', { isSignal: true }] }], viewport: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkVirtualScrollViewport), { isSignal: true }] }] } });
10958
11941
 
10959
11942
  let nextId$1 = 0;
10960
11943
  /**
@@ -11179,7 +12162,7 @@ class NhAssistantPanelComponent {
11179
12162
  void this.store.cancel();
11180
12163
  }
11181
12164
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
11182
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", type: NhAssistantPanelComponent, isStandalone: true, selector: "nh-assistant-panel", viewQueries: [{ propertyName: "drawer", first: true, predicate: ["drawer"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template #drawer>\n <section class=\"drawer\" role=\"dialog\" aria-modal=\"false\" [attr.aria-labelledby]=\"titleId\">\n <header class=\"header\">\n <div class=\"heading\">\n <h2 [id]=\"titleId\">{{ 'nh-assistant.panel.title' | translate }}</h2>\n </div>\n <div class=\"header-actions\">\n @if (store.enabled()) {\n @if (push.state() !== 'unavailable') {\n <button\n class=\"icon-button notification-button\"\n type=\"button\"\n [class.active]=\"push.state() === 'on'\"\n [class.off]=\"!push.settings()?.pushEnabled\"\n [class.blocked]=\"push.state() === 'blocked'\"\n [disabled]=\"push.busy()\"\n [attr.aria-pressed]=\"push.settings()?.pushEnabled === true\"\n [attr.aria-label]=\"notificationsLabel() | translate\"\n [attr.title]=\"('nh-assistant.preferences.notifications-state.' + push.state() | translate) + ' \u00B7 ' + (notificationsLabel() | translate)\"\n (click)=\"toggleNotifications()\">\n <nh-assistant-icon name=\"bell\" />\n </button>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [class.active]=\"showConversations()\"\n [attr.aria-expanded]=\"showConversations()\"\n [attr.aria-label]=\"(showConversations() ? 'nh-assistant.panel.hide-conversations' : 'nh-assistant.panel.show-conversations') | translate\"\n [attr.title]=\"(showConversations() ? 'nh-assistant.panel.hide-conversations' : 'nh-assistant.panel.show-conversations') | translate\"\n (click)=\"toggleConversations()\">\n <nh-assistant-icon name=\"list\" />\n </button>\n <button\n class=\"icon-button\"\n type=\"button\"\n [class.active]=\"showPreferences()\"\n [attr.aria-expanded]=\"showPreferences()\"\n [attr.aria-label]=\"'nh-assistant.panel.open-preferences' | translate\"\n [attr.title]=\"'nh-assistant.panel.open-preferences' | translate\"\n (click)=\"togglePreferences()\">\n <nh-assistant-icon name=\"settings\" />\n </button>\n @if (adminRoute && store.canAdminister()) {\n <a\n class=\"icon-button\"\n [routerLink]=\"adminRoute\"\n [attr.aria-label]=\"'nh-assistant.panel.open-admin' | translate\"\n [attr.title]=\"'nh-assistant.panel.open-admin' | translate\"\n (click)=\"panel.close()\">\n <nh-assistant-icon name=\"admin\" />\n </a>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.new-conversation' | translate\"\n [attr.title]=\"'nh-assistant.panel.new-conversation' | translate\"\n (click)=\"newConversation()\">\n <nh-assistant-icon name=\"plus\" />\n </button>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.close' | translate\"\n [attr.title]=\"'nh-assistant.panel.close' | translate\"\n (click)=\"panel.close()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n </header>\n\n @if (store.enabled()) {\n <nh-assistant-activity [items]=\"store.activity()\" (conversationSelect)=\"openConversation($event)\" />\n }\n\n <div class=\"body\">\n @if (store.statusLoading() && !store.enabled()) {\n <div class=\"state\" role=\"status\">\n <p class=\"state-title\">{{ 'nh-assistant.panel.loading' | translate }}</p>\n </div>\n } @else if (!store.enabled()) {\n <div class=\"state\">\n <nh-assistant-icon class=\"state-icon\" name=\"warning\" />\n <p class=\"state-title\">{{ 'nh-assistant.panel.unavailable-title' | translate }}</p>\n <p>{{ 'nh-assistant.panel.unavailable-description' | translate }}</p>\n </div>\n } @else {\n @if (showPreferences()) {\n <nh-assistant-preferences class=\"overlay-view\" (closed)=\"showPreferences.set(false)\" />\n }\n\n @if (showShare()) {\n <nh-assistant-share class=\"overlay-view\" [section]=\"shareSection()\" (closed)=\"showShare.set(false)\" />\n }\n\n @if (showConversations()) {\n <nh-assistant-conversation-list\n class=\"overlay-view\"\n [conversations]=\"store.conversations()\"\n [total]=\"store.conversationsTotal()\"\n [activeConversationId]=\"conversation()?.id ?? null\"\n [loading]=\"store.conversationsLoading()\"\n (conversationSelect)=\"openConversation($event)\"\n (conversationDelete)=\"deleteConversation($event)\"\n (conversationCreate)=\"newConversation()\" />\n }\n\n @if (store.conversationLoading()) {\n <div class=\"state\" role=\"status\">\n <p class=\"state-title\">{{ 'nh-assistant.thread.loading' | translate }}</p>\n </div>\n } @else if (messages().length === 0 && !store.streaming()) {\n <div class=\"state\">\n <nh-assistant-icon class=\"state-icon\" name=\"assistant\" />\n <p class=\"state-title\">{{ 'nh-assistant.panel.empty-title' | translate }}</p>\n <p>{{ 'nh-assistant.panel.empty-description' | translate }}</p>\n </div>\n } @else {\n <nh-assistant-thread\n class=\"thread\"\n [attr.inert]=\"showPreferences() || showShare() || showConversations() ? '' : null\"\n [messages]=\"messages()\"\n [streaming]=\"busy()\"\n [deciding]=\"store.deciding()\"\n [markdown]=\"markdown\"\n [members]=\"store.members()\"\n [currentActorId]=\"conversation()?.currentActorId ?? null\"\n [canDecide]=\"store.canDecide()\"\n [decisionOwner]=\"activeMemberName()\"\n [approvalRights]=\"store.approvalRights()\"\n (approvalChoice)=\"decideApproval($event)\"\n (retryMessage)=\"store.send($event)\" />\n }\n }\n </div>\n\n @if (notificationError() ?? store.error(); as error) {\n <div class=\"error\" role=\"alert\">\n <nh-assistant-icon name=\"warning\" />\n <p>{{ errorKeys() | nhAssistantTranslate: error.code }}</p>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.dismiss-error' | translate\"\n [attr.title]=\"'nh-assistant.panel.dismiss-error' | translate\"\n (click)=\"store.clearError(); notificationError.set(null)\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n } @else if (store.notice(); as notice) {\n <div class=\"turn-notice\" role=\"status\">\n <nh-assistant-icon name=\"warning\" />\n <p>{{ noticeKeys() | nhAssistantTranslate: notice.code }}</p>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.dismiss-error' | translate\"\n [attr.title]=\"'nh-assistant.panel.dismiss-error' | translate\"\n (click)=\"store.clearNotice()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n }\n\n @if (store.enabled() && !showPreferences() && !showShare()) {\n <footer class=\"footer\" (focusin)=\"refreshPageContext()\">\n @if (closed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.conversation-closed' | translate }}</p>\n } @else if (lastTurnFailed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.last-turn-failed' | translate }}</p>\n }\n @if (store.collaboration() || store.pageContext()) {\n <div class=\"conversation-actions\">\n @if (store.collaboration()) {\n @if (!conversation() || store.isOwner()) {\n <button class=\"icon-button\" type=\"button\" [disabled]=\"!canShare()\"\n [attr.aria-label]=\"'nh-assistant.panel.share' | translate\"\n [attr.title]=\"'nh-assistant.panel.share' | translate\"\n [attr.aria-expanded]=\"showShare() && shareSection() === 'link'\"\n (click)=\"toggleShare('link')\">\n <nh-assistant-icon name=\"link\" />\n </button>\n }\n <button class=\"icon-button\" type=\"button\" [disabled]=\"!canShare()\"\n [class.active]=\"shared()\"\n [attr.aria-label]=\"(store.isOwner() || !conversation() ? 'nh-assistant.panel.add-members' : 'nh-assistant.panel.members') | translate\"\n [attr.title]=\"(store.isOwner() || !conversation() ? 'nh-assistant.panel.add-members' : 'nh-assistant.panel.members') | translate\"\n [attr.aria-expanded]=\"showShare() && shareSection() === 'members'\"\n (click)=\"toggleShare('members')\">\n <nh-assistant-icon name=\"users\" />\n </button>\n }\n @if (store.pageContext(); as context) {\n <button\n class=\"icon-button page-context-button\"\n [class.active]=\"!store.pageContextExcluded()\"\n type=\"button\"\n [attr.aria-pressed]=\"!store.pageContextExcluded()\"\n [attr.aria-label]=\"(store.pageContextExcluded() ? 'nh-assistant.page-context.include' : 'nh-assistant.page-context.exclude') | translate: { page: describeContext(context) }\"\n [attr.title]=\"(store.pageContextExcluded() ? 'nh-assistant.page-context.include' : 'nh-assistant.page-context.exclude') | translate: { page: describeContext(context) }\"\n (click)=\"store.setPageContextExcluded(!store.pageContextExcluded())\">\n <nh-assistant-icon name=\"page\" />\n </button>\n }\n </div>\n }\n @if (store.queuedMessages().length > 0) {\n <section class=\"message-queue\" [attr.aria-label]=\"'nh-assistant.queue.title' | translate: { count: store.queuedMessages().length }\">\n <div class=\"queue-heading\">\n <span aria-live=\"polite\">{{ 'nh-assistant.queue.title' | translate: { count: store.queuedMessages().length } }}</span>\n @if (store.queuePaused()) {\n <button type=\"button\" class=\"queue-resume\" [disabled]=\"editingQueuedId() !== null\" (click)=\"store.resumeQueue()\">\n {{ 'nh-assistant.queue.resume' | translate }}\n </button>\n }\n </div>\n <ol class=\"queue-items\">\n @for (item of store.queuedMessages(); track item.id) {\n <li class=\"queue-item\" [class.failed]=\"item.state === 'failed'\">\n @if (editingQueuedId() === item.id && item.state !== 'sending') {\n <div class=\"queue-edit\">\n <textarea rows=\"2\" [value]=\"queuedDraft()\" (input)=\"changeQueuedDraft($event)\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\"></textarea>\n <div class=\"queue-buttons\">\n <button type=\"button\" class=\"icon-button\" [disabled]=\"!validQueuedDraft()\"\n [attr.aria-label]=\"'nh-assistant.queue.save' | translate\" [attr.title]=\"'nh-assistant.queue.save' | translate\"\n (click)=\"saveQueuedMessage(item.id)\"><nh-assistant-icon name=\"check\" /></button>\n <button type=\"button\" class=\"icon-button\" [attr.aria-label]=\"'nh-assistant.queue.cancel-edit' | translate\"\n [attr.title]=\"'nh-assistant.queue.cancel-edit' | translate\"\n (click)=\"cancelQueueEdit()\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n </div>\n } @else {\n <div class=\"queue-message\">\n <p [title]=\"item.text\">{{ item.text }}</p>\n @if (item.files?.length) {\n <span class=\"queue-state queue-files\" data-queue-files>{{ 'nh-assistant.queue.files' | translate: { count: item.files!.length, names: fileNames(item.files!) } }}</span>\n }\n @if (item.state !== 'queued') {\n <span class=\"queue-state\">{{ 'nh-assistant.queue.state.' + item.state | translate }}</span>\n }\n </div>\n <div class=\"queue-buttons\">\n @if (store.canSteer() && item.state === 'queued') {\n <button type=\"button\" class=\"queue-steer\" [disabled]=\"editingQueuedId() !== null\"\n [attr.title]=\"'nh-assistant.queue.steer-hint' | translate\"\n (click)=\"store.steerQueuedMessage(item.id)\">{{ 'nh-assistant.queue.steer' | translate }}</button>\n }\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\" [attr.title]=\"'nh-assistant.queue.edit' | translate\"\n (click)=\"editQueuedMessage(item.id, item.text)\"><nh-assistant-icon name=\"edit\" /></button>\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.remove' | translate\" [attr.title]=\"'nh-assistant.queue.remove' | translate\"\n (click)=\"store.removeQueuedMessage(item.id)\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n }\n </li>\n }\n </ol>\n </section>\n }\n <nh-assistant-composer\n [disabled]=\"closed()\"\n [sendDisabled]=\"composerDisabled()\"\n [busy]=\"busy()\"\n [queueWhileBusy]=\"true\"\n [status]=\"composerStatus()\"\n [statusActor]=\"activeMemberName()\"\n [canStop]=\"canStop()\"\n [maxLength]=\"store.limits()?.maxMessageChars ?? null\"\n [restore]=\"store.restoredDraft()\"\n [uploads]=\"store.uploads()\"\n [attachments]=\"store.attachments()\"\n (restored)=\"store.consumeRestoredDraft()\"\n (send)=\"send($event)\"\n (attach)=\"attach($event)\"\n (removeAttachment)=\"store.removeAttachment($event)\"\n (cancel)=\"cancel()\">\n @if (store.agents().length > 1) {\n <nh-assistant-agent-picker\n nhAssistantComposerControls\n [compact]=\"true\"\n [agents]=\"store.agents()\"\n [selectedAgentId]=\"store.selectedAgentId()\"\n (agentChange)=\"store.selectAgent($event)\" />\n }\n </nh-assistant-composer>\n </footer>\n }\n </section>\n</ng-template>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}.drawer{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){.drawer{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:contents}.drawer{position:relative;display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:100%;border-left:1px solid var(--nha-border);color:var(--nha-text);background:var(--nha-background);box-shadow:var(--nha-shadow);font-family:var(--nha-font);font-size:14px}.drawer *,.drawer *:before,.drawer *:after{box-sizing:border-box}.header{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px 12px;border-bottom:1px solid var(--nha-border);padding:12px 12px 12px 16px}.heading{display:contents}h2{grid-row:1;min-width:0;margin:0;overflow:hidden;font-size:16px;font-weight:750;letter-spacing:-.01em;text-overflow:ellipsis;white-space:nowrap}.header-actions{display:flex;flex:none;grid-column:2;grid-row:1;gap:2px;min-width:0}.header-actions .icon-button{width:32px;height:32px}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:var(--nha-radius-control);color:var(--nha-text-muted);background:transparent;cursor:pointer;text-decoration:none}.icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.icon-button:disabled{cursor:not-allowed;opacity:.5}.icon-button.active{color:var(--nha-highlight);background:var(--nha-surface-strong)}.body{position:relative;display:flex;flex:1;flex-direction:column;min-height:0;background:var(--nha-background)}.thread{flex:1;min-height:0}.overlay-view{position:absolute;z-index:1;inset:0;border-bottom:1px solid var(--nha-border);background:var(--nha-background)}.state{display:flex;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:24px;color:var(--nha-text-muted);text-align:center}.state p{max-width:32ch;margin:0;font-size:13px}.state-icon{margin-bottom:8px;color:var(--nha-highlight);font-size:28px}.state .state-title{color:var(--nha-text);font-size:15px;font-weight:700}.error{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-danger);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-danger);background:var(--nha-danger-soft)}.error p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.turn-notice{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-warning);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-warning);background:var(--nha-warning-soft)}.turn-notice p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.footer{padding:12px 16px 14px}.conversation-actions{display:flex;align-items:center;gap:4px;margin:0 -4px 6px}.page-context-button{margin-left:auto}.message-queue{margin-bottom:10px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);background:var(--nha-background);overflow:hidden;font-size:12px}.queue-heading{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:8px 10px;color:var(--nha-text-muted)}.queue-items{max-height:180px;overflow-y:auto;margin:0;padding:0;list-style:none}.queue-item{display:flex;align-items:center;gap:8px;border-top:1px solid var(--nha-border);padding:6px 8px 6px 10px}.queue-item.failed{border-left:2px solid var(--nha-danger)}.queue-message{flex:1;min-width:0}.queue-message p{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere;margin:0}.queue-state{color:var(--nha-text-muted);font-size:11px}.queue-files{display:block;overflow-wrap:anywhere}.queue-buttons{display:flex;flex-shrink:0;align-items:center;gap:2px}.queue-buttons .icon-button{width:28px;height:30px}.queue-resume,.queue-steer{border:0;border-radius:6px;padding:6px;color:var(--nha-highlight);background:transparent;font:inherit;cursor:pointer}.queue-resume:hover,.queue-steer:hover{background:var(--nha-surface)}.queue-resume:focus-visible,.queue-steer:focus-visible{outline:2px solid var(--nha-focus)}.queue-resume:disabled,.queue-steer:disabled{cursor:not-allowed;opacity:.5}.queue-edit{width:100%}.queue-edit textarea{box-sizing:border-box;width:100%;border:1px solid var(--nha-border);border-radius:6px;padding:8px;color:var(--nha-text);background:var(--nha-surface);font:inherit;resize:vertical}.queue-edit .queue-buttons{justify-content:flex-end}.notification-button{position:relative}.notification-button.off:after{content:\"\";position:absolute;width:18px;height:2px;border-radius:1px;background:currentColor;transform:rotate(-45deg)}.notification-button.blocked{color:var(--nha-warning)}@media(max-width:359px){.header{gap:6px;padding-inline:12px}.header-actions .icon-button{width:28px;height:32px}}.notice{margin:0 0 8px;color:var(--nha-text-muted);font-size:12px}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "component", type: NhAssistantActivityComponent, selector: "nh-assistant-activity", inputs: ["items"], outputs: ["conversationSelect"] }, { kind: "component", type: NhAssistantAgentPickerComponent, selector: "nh-assistant-agent-picker", inputs: ["agents", "selectedAgentId", "disabled", "compact"], outputs: ["agentChange"] }, { kind: "component", type: NhAssistantComposerComponent, selector: "nh-assistant-composer", inputs: ["disabled", "sendDisabled", "busy", "queueWhileBusy", "status", "statusActor", "canStop", "maxLength", "restore", "uploads", "attachments"], outputs: ["send", "cancel", "restored", "attach", "removeAttachment"] }, { kind: "component", type: NhAssistantConversationListComponent, selector: "nh-assistant-conversation-list", inputs: ["conversations", "total", "activeConversationId", "loading", "disabled"], outputs: ["conversationSelect", "conversationDelete", "conversationCreate"] }, { kind: "component", type: NhAssistantPreferencesComponent, selector: "nh-assistant-preferences", outputs: ["closed", "saved"] }, { kind: "component", type: NhAssistantShareComponent, selector: "nh-assistant-share", inputs: ["section"], outputs: ["closed"] }, { kind: "component", type: NhAssistantThreadComponent, selector: "nh-assistant-thread", inputs: ["messages", "streaming", "deciding", "markdown", "members", "currentActorId", "canDecide", "decisionOwner", "approvalRights"], outputs: ["approvalDecision", "approvalChoice", "retryMessage"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
12165
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", type: NhAssistantPanelComponent, isStandalone: true, selector: "nh-assistant-panel", viewQueries: [{ propertyName: "drawer", first: true, predicate: ["drawer"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template #drawer>\n <section class=\"drawer\" role=\"dialog\" aria-modal=\"false\" [attr.aria-labelledby]=\"titleId\">\n <header class=\"header\">\n <div class=\"heading\">\n <h2 [id]=\"titleId\">{{ 'nh-assistant.panel.title' | translate }}</h2>\n </div>\n <div class=\"header-actions\">\n @if (store.enabled()) {\n @if (push.state() !== 'unavailable') {\n <button\n class=\"icon-button notification-button\"\n type=\"button\"\n [class.active]=\"push.state() === 'on'\"\n [class.off]=\"!push.settings()?.pushEnabled\"\n [class.blocked]=\"push.state() === 'blocked'\"\n [disabled]=\"push.busy()\"\n [attr.aria-pressed]=\"push.settings()?.pushEnabled === true\"\n [attr.aria-label]=\"notificationsLabel() | translate\"\n [attr.title]=\"('nh-assistant.preferences.notifications-state.' + push.state() | translate) + ' \u00B7 ' + (notificationsLabel() | translate)\"\n (click)=\"toggleNotifications()\">\n <nh-assistant-icon name=\"bell\" />\n </button>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [class.active]=\"showConversations()\"\n [attr.aria-expanded]=\"showConversations()\"\n [attr.aria-label]=\"(showConversations() ? 'nh-assistant.panel.hide-conversations' : 'nh-assistant.panel.show-conversations') | translate\"\n [attr.title]=\"(showConversations() ? 'nh-assistant.panel.hide-conversations' : 'nh-assistant.panel.show-conversations') | translate\"\n (click)=\"toggleConversations()\">\n <nh-assistant-icon name=\"list\" />\n </button>\n <button\n class=\"icon-button\"\n type=\"button\"\n [class.active]=\"showPreferences()\"\n [attr.aria-expanded]=\"showPreferences()\"\n [attr.aria-label]=\"'nh-assistant.panel.open-preferences' | translate\"\n [attr.title]=\"'nh-assistant.panel.open-preferences' | translate\"\n (click)=\"togglePreferences()\">\n <nh-assistant-icon name=\"settings\" />\n </button>\n @if (adminRoute && store.canAdminister()) {\n <a\n class=\"icon-button\"\n [routerLink]=\"adminRoute\"\n [attr.aria-label]=\"'nh-assistant.panel.open-admin' | translate\"\n [attr.title]=\"'nh-assistant.panel.open-admin' | translate\"\n (click)=\"panel.close()\">\n <nh-assistant-icon name=\"admin\" />\n </a>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.new-conversation' | translate\"\n [attr.title]=\"'nh-assistant.panel.new-conversation' | translate\"\n (click)=\"newConversation()\">\n <nh-assistant-icon name=\"plus\" />\n </button>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.close' | translate\"\n [attr.title]=\"'nh-assistant.panel.close' | translate\"\n (click)=\"panel.close()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n </header>\n\n @if (store.enabled()) {\n <nh-assistant-activity [items]=\"store.activity()\" (conversationSelect)=\"openConversation($event)\" />\n }\n\n <div class=\"body\">\n @if (store.statusLoading() && !store.enabled()) {\n <div class=\"state\" role=\"status\">\n <p class=\"state-title\">{{ 'nh-assistant.panel.loading' | translate }}</p>\n </div>\n } @else if (!store.enabled()) {\n <div class=\"state\">\n <nh-assistant-icon class=\"state-icon\" name=\"warning\" />\n <p class=\"state-title\">{{ 'nh-assistant.panel.unavailable-title' | translate }}</p>\n <p>{{ 'nh-assistant.panel.unavailable-description' | translate }}</p>\n </div>\n } @else {\n @if (showPreferences()) {\n <nh-assistant-preferences class=\"overlay-view\" (closed)=\"showPreferences.set(false)\" />\n }\n\n @if (showShare()) {\n <nh-assistant-share class=\"overlay-view\" [section]=\"shareSection()\" (closed)=\"showShare.set(false)\" />\n }\n\n @if (showConversations()) {\n <nh-assistant-conversation-list\n class=\"overlay-view\"\n [conversations]=\"store.conversations()\"\n [total]=\"store.conversationsTotal()\"\n [activeConversationId]=\"conversation()?.id ?? null\"\n [loading]=\"store.conversationsLoading()\"\n (conversationSelect)=\"openConversation($event)\"\n (conversationDelete)=\"deleteConversation($event)\"\n (conversationCreate)=\"newConversation()\" />\n }\n\n @if (store.conversationLoading()) {\n <div class=\"state\" role=\"status\">\n <p class=\"state-title\">{{ 'nh-assistant.thread.loading' | translate }}</p>\n </div>\n } @else if (messages().length === 0 && !store.streaming()) {\n <div class=\"state\">\n <nh-assistant-icon class=\"state-icon\" name=\"assistant\" />\n <p class=\"state-title\">{{ 'nh-assistant.panel.empty-title' | translate }}</p>\n <p>{{ 'nh-assistant.panel.empty-description' | translate }}</p>\n </div>\n } @else {\n <nh-assistant-thread\n class=\"thread\"\n [attr.inert]=\"showPreferences() || showShare() || showConversations() ? '' : null\"\n [messages]=\"messages()\"\n [streaming]=\"busy()\"\n [deciding]=\"store.deciding()\"\n [markdown]=\"markdown\"\n [members]=\"store.members()\"\n [currentActorId]=\"conversation()?.currentActorId ?? null\"\n [canDecide]=\"store.canDecide()\"\n [decisionOwner]=\"activeMemberName()\"\n [approvalRights]=\"store.approvalRights()\"\n [writingMessageId]=\"store.writingMessageId()\"\n (approvalChoice)=\"decideApproval($event)\"\n (retryMessage)=\"store.send($event)\" />\n }\n }\n </div>\n\n @if (notificationError() ?? store.error(); as error) {\n <div class=\"error\" role=\"alert\">\n <nh-assistant-icon name=\"warning\" />\n <p>{{ errorKeys() | nhAssistantTranslate: error.code }}</p>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.dismiss-error' | translate\"\n [attr.title]=\"'nh-assistant.panel.dismiss-error' | translate\"\n (click)=\"store.clearError(); notificationError.set(null)\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n } @else if (store.notice(); as notice) {\n <div class=\"turn-notice\" role=\"status\">\n <nh-assistant-icon name=\"warning\" />\n <p>{{ noticeKeys() | nhAssistantTranslate: notice.code }}</p>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.dismiss-error' | translate\"\n [attr.title]=\"'nh-assistant.panel.dismiss-error' | translate\"\n (click)=\"store.clearNotice()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n }\n\n @if (store.enabled() && !showPreferences() && !showShare()) {\n <footer class=\"footer\" (focusin)=\"refreshPageContext()\">\n @if (closed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.conversation-closed' | translate }}</p>\n } @else if (lastTurnFailed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.last-turn-failed' | translate }}</p>\n }\n @if (store.collaboration() || store.pageContext()) {\n <div class=\"conversation-actions\">\n @if (store.collaboration()) {\n @if (!conversation() || store.isOwner()) {\n <button class=\"icon-button\" type=\"button\" [disabled]=\"!canShare()\"\n [attr.aria-label]=\"'nh-assistant.panel.share' | translate\"\n [attr.title]=\"'nh-assistant.panel.share' | translate\"\n [attr.aria-expanded]=\"showShare() && shareSection() === 'link'\"\n (click)=\"toggleShare('link')\">\n <nh-assistant-icon name=\"link\" />\n </button>\n }\n <button class=\"icon-button\" type=\"button\" [disabled]=\"!canShare()\"\n [class.active]=\"shared()\"\n [attr.aria-label]=\"(store.isOwner() || !conversation() ? 'nh-assistant.panel.add-members' : 'nh-assistant.panel.members') | translate\"\n [attr.title]=\"(store.isOwner() || !conversation() ? 'nh-assistant.panel.add-members' : 'nh-assistant.panel.members') | translate\"\n [attr.aria-expanded]=\"showShare() && shareSection() === 'members'\"\n (click)=\"toggleShare('members')\">\n <nh-assistant-icon name=\"users\" />\n </button>\n }\n @if (store.pageContext(); as context) {\n <button\n class=\"icon-button page-context-button\"\n [class.active]=\"!store.pageContextExcluded()\"\n type=\"button\"\n [attr.aria-pressed]=\"!store.pageContextExcluded()\"\n [attr.aria-label]=\"(store.pageContextExcluded() ? 'nh-assistant.page-context.include' : 'nh-assistant.page-context.exclude') | translate: { page: describeContext(context) }\"\n [attr.title]=\"(store.pageContextExcluded() ? 'nh-assistant.page-context.include' : 'nh-assistant.page-context.exclude') | translate: { page: describeContext(context) }\"\n (click)=\"store.setPageContextExcluded(!store.pageContextExcluded())\">\n <nh-assistant-icon name=\"page\" />\n </button>\n }\n </div>\n }\n @if (store.queuedMessages().length > 0) {\n <section class=\"message-queue\" [attr.aria-label]=\"'nh-assistant.queue.title' | translate: { count: store.queuedMessages().length }\">\n <div class=\"queue-heading\">\n <span aria-live=\"polite\">{{ 'nh-assistant.queue.title' | translate: { count: store.queuedMessages().length } }}</span>\n @if (store.queuePaused()) {\n <button type=\"button\" class=\"queue-resume\" [disabled]=\"editingQueuedId() !== null\" (click)=\"store.resumeQueue()\">\n {{ 'nh-assistant.queue.resume' | translate }}\n </button>\n }\n </div>\n <ol class=\"queue-items\">\n @for (item of store.queuedMessages(); track item.id) {\n <li class=\"queue-item\" [class.failed]=\"item.state === 'failed'\">\n @if (editingQueuedId() === item.id && item.state !== 'sending') {\n <div class=\"queue-edit\">\n <textarea rows=\"2\" [value]=\"queuedDraft()\" (input)=\"changeQueuedDraft($event)\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\"></textarea>\n <div class=\"queue-buttons\">\n <button type=\"button\" class=\"icon-button\" [disabled]=\"!validQueuedDraft()\"\n [attr.aria-label]=\"'nh-assistant.queue.save' | translate\" [attr.title]=\"'nh-assistant.queue.save' | translate\"\n (click)=\"saveQueuedMessage(item.id)\"><nh-assistant-icon name=\"check\" /></button>\n <button type=\"button\" class=\"icon-button\" [attr.aria-label]=\"'nh-assistant.queue.cancel-edit' | translate\"\n [attr.title]=\"'nh-assistant.queue.cancel-edit' | translate\"\n (click)=\"cancelQueueEdit()\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n </div>\n } @else {\n <div class=\"queue-message\">\n <p [title]=\"item.text\">{{ item.text }}</p>\n @if (item.files?.length) {\n <span class=\"queue-state queue-files\" data-queue-files>{{ 'nh-assistant.queue.files' | translate: { count: item.files!.length, names: fileNames(item.files!) } }}</span>\n }\n @if (item.state !== 'queued') {\n <span class=\"queue-state\">{{ 'nh-assistant.queue.state.' + item.state | translate }}</span>\n }\n </div>\n <div class=\"queue-buttons\">\n @if (store.canSteer() && item.state === 'queued') {\n <button type=\"button\" class=\"queue-steer\" [disabled]=\"editingQueuedId() !== null\"\n [attr.title]=\"'nh-assistant.queue.steer-hint' | translate\"\n (click)=\"store.steerQueuedMessage(item.id)\">{{ 'nh-assistant.queue.steer' | translate }}</button>\n }\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\" [attr.title]=\"'nh-assistant.queue.edit' | translate\"\n (click)=\"editQueuedMessage(item.id, item.text)\"><nh-assistant-icon name=\"edit\" /></button>\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.remove' | translate\" [attr.title]=\"'nh-assistant.queue.remove' | translate\"\n (click)=\"store.removeQueuedMessage(item.id)\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n }\n </li>\n }\n </ol>\n </section>\n }\n <nh-assistant-composer\n [disabled]=\"closed()\"\n [sendDisabled]=\"composerDisabled()\"\n [busy]=\"busy()\"\n [queueWhileBusy]=\"true\"\n [status]=\"composerStatus()\"\n [statusActor]=\"activeMemberName()\"\n [canStop]=\"canStop()\"\n [maxLength]=\"store.limits()?.maxMessageChars ?? null\"\n [restore]=\"store.restoredDraft()\"\n [uploads]=\"store.uploads()\"\n [attachments]=\"store.attachments()\"\n (restored)=\"store.consumeRestoredDraft()\"\n (send)=\"send($event)\"\n (attach)=\"attach($event)\"\n (removeAttachment)=\"store.removeAttachment($event)\"\n (cancel)=\"cancel()\">\n @if (store.agents().length > 1) {\n <nh-assistant-agent-picker\n nhAssistantComposerControls\n [compact]=\"true\"\n [agents]=\"store.agents()\"\n [selectedAgentId]=\"store.selectedAgentId()\"\n (agentChange)=\"store.selectAgent($event)\" />\n }\n </nh-assistant-composer>\n </footer>\n }\n </section>\n</ng-template>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}.drawer{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){.drawer{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:contents}.drawer{position:relative;display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:100%;border-left:1px solid var(--nha-border);color:var(--nha-text);background:var(--nha-background);box-shadow:var(--nha-shadow);font-family:var(--nha-font);font-size:14px}.drawer *,.drawer *:before,.drawer *:after{box-sizing:border-box}.header{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px 12px;border-bottom:1px solid var(--nha-border);padding:12px 12px 12px 16px}.heading{display:contents}h2{grid-row:1;min-width:0;margin:0;overflow:hidden;font-size:16px;font-weight:750;letter-spacing:-.01em;text-overflow:ellipsis;white-space:nowrap}.header-actions{display:flex;flex:none;grid-column:2;grid-row:1;gap:2px;min-width:0}.header-actions .icon-button{width:32px;height:32px}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:var(--nha-radius-control);color:var(--nha-text-muted);background:transparent;cursor:pointer;text-decoration:none}.icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.icon-button:disabled{cursor:not-allowed;opacity:.5}.icon-button.active{color:var(--nha-highlight);background:var(--nha-surface-strong)}.body{position:relative;display:flex;flex:1;flex-direction:column;min-height:0;background:var(--nha-background)}.thread{flex:1;min-height:0}.overlay-view{position:absolute;z-index:1;inset:0;border-bottom:1px solid var(--nha-border);background:var(--nha-background)}.state{display:flex;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:24px;color:var(--nha-text-muted);text-align:center}.state p{max-width:32ch;margin:0;font-size:13px}.state-icon{margin-bottom:8px;color:var(--nha-highlight);font-size:28px}.state .state-title{color:var(--nha-text);font-size:15px;font-weight:700}.error{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-danger);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-danger);background:var(--nha-danger-soft)}.error p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.turn-notice{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-warning);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-warning);background:var(--nha-warning-soft)}.turn-notice p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.footer{padding:12px 16px 14px}.conversation-actions{display:flex;align-items:center;gap:4px;margin:0 -4px 6px}.page-context-button{margin-left:auto}.message-queue{margin-bottom:10px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);background:var(--nha-background);overflow:hidden;font-size:12px}.queue-heading{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:8px 10px;color:var(--nha-text-muted)}.queue-items{max-height:180px;overflow-y:auto;margin:0;padding:0;list-style:none}.queue-item{display:flex;align-items:center;gap:8px;border-top:1px solid var(--nha-border);padding:6px 8px 6px 10px}.queue-item.failed{border-left:2px solid var(--nha-danger)}.queue-message{flex:1;min-width:0}.queue-message p{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere;margin:0}.queue-state{color:var(--nha-text-muted);font-size:11px}.queue-files{display:block;overflow-wrap:anywhere}.queue-buttons{display:flex;flex-shrink:0;align-items:center;gap:2px}.queue-buttons .icon-button{width:28px;height:30px}.queue-resume,.queue-steer{border:0;border-radius:6px;padding:6px;color:var(--nha-highlight);background:transparent;font:inherit;cursor:pointer}.queue-resume:hover,.queue-steer:hover{background:var(--nha-surface)}.queue-resume:focus-visible,.queue-steer:focus-visible{outline:2px solid var(--nha-focus)}.queue-resume:disabled,.queue-steer:disabled{cursor:not-allowed;opacity:.5}.queue-edit{width:100%}.queue-edit textarea{box-sizing:border-box;width:100%;border:1px solid var(--nha-border);border-radius:6px;padding:8px;color:var(--nha-text);background:var(--nha-surface);font:inherit;resize:vertical}.queue-edit .queue-buttons{justify-content:flex-end}.notification-button{position:relative}.notification-button.off:after{content:\"\";position:absolute;width:18px;height:2px;border-radius:1px;background:currentColor;transform:rotate(-45deg)}.notification-button.blocked{color:var(--nha-warning)}@media(max-width:359px){.header{gap:6px;padding-inline:12px}.header-actions .icon-button{width:28px;height:32px}}.notice{margin:0 0 8px;color:var(--nha-text-muted);font-size:12px}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "component", type: NhAssistantActivityComponent, selector: "nh-assistant-activity", inputs: ["items"], outputs: ["conversationSelect"] }, { kind: "component", type: NhAssistantAgentPickerComponent, selector: "nh-assistant-agent-picker", inputs: ["agents", "selectedAgentId", "disabled", "compact"], outputs: ["agentChange"] }, { kind: "component", type: NhAssistantComposerComponent, selector: "nh-assistant-composer", inputs: ["disabled", "sendDisabled", "busy", "queueWhileBusy", "status", "statusActor", "canStop", "maxLength", "restore", "uploads", "attachments"], outputs: ["send", "cancel", "restored", "attach", "removeAttachment"] }, { kind: "component", type: NhAssistantConversationListComponent, selector: "nh-assistant-conversation-list", inputs: ["conversations", "total", "activeConversationId", "loading", "disabled"], outputs: ["conversationSelect", "conversationDelete", "conversationCreate"] }, { kind: "component", type: NhAssistantPreferencesComponent, selector: "nh-assistant-preferences", outputs: ["closed", "saved"] }, { kind: "component", type: NhAssistantShareComponent, selector: "nh-assistant-share", inputs: ["section"], outputs: ["closed"] }, { kind: "component", type: NhAssistantThreadComponent, selector: "nh-assistant-thread", inputs: ["messages", "streaming", "deciding", "markdown", "members", "currentActorId", "canDecide", "decisionOwner", "approvalRights", "writingMessageId"], outputs: ["approvalDecision", "approvalChoice", "retryMessage"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11183
12166
  }
11184
12167
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantPanelComponent, decorators: [{
11185
12168
  type: Component,
@@ -11195,7 +12178,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
11195
12178
  NhAssistantPreferencesComponent,
11196
12179
  NhAssistantShareComponent,
11197
12180
  NhAssistantThreadComponent
11198
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-template #drawer>\n <section class=\"drawer\" role=\"dialog\" aria-modal=\"false\" [attr.aria-labelledby]=\"titleId\">\n <header class=\"header\">\n <div class=\"heading\">\n <h2 [id]=\"titleId\">{{ 'nh-assistant.panel.title' | translate }}</h2>\n </div>\n <div class=\"header-actions\">\n @if (store.enabled()) {\n @if (push.state() !== 'unavailable') {\n <button\n class=\"icon-button notification-button\"\n type=\"button\"\n [class.active]=\"push.state() === 'on'\"\n [class.off]=\"!push.settings()?.pushEnabled\"\n [class.blocked]=\"push.state() === 'blocked'\"\n [disabled]=\"push.busy()\"\n [attr.aria-pressed]=\"push.settings()?.pushEnabled === true\"\n [attr.aria-label]=\"notificationsLabel() | translate\"\n [attr.title]=\"('nh-assistant.preferences.notifications-state.' + push.state() | translate) + ' \u00B7 ' + (notificationsLabel() | translate)\"\n (click)=\"toggleNotifications()\">\n <nh-assistant-icon name=\"bell\" />\n </button>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [class.active]=\"showConversations()\"\n [attr.aria-expanded]=\"showConversations()\"\n [attr.aria-label]=\"(showConversations() ? 'nh-assistant.panel.hide-conversations' : 'nh-assistant.panel.show-conversations') | translate\"\n [attr.title]=\"(showConversations() ? 'nh-assistant.panel.hide-conversations' : 'nh-assistant.panel.show-conversations') | translate\"\n (click)=\"toggleConversations()\">\n <nh-assistant-icon name=\"list\" />\n </button>\n <button\n class=\"icon-button\"\n type=\"button\"\n [class.active]=\"showPreferences()\"\n [attr.aria-expanded]=\"showPreferences()\"\n [attr.aria-label]=\"'nh-assistant.panel.open-preferences' | translate\"\n [attr.title]=\"'nh-assistant.panel.open-preferences' | translate\"\n (click)=\"togglePreferences()\">\n <nh-assistant-icon name=\"settings\" />\n </button>\n @if (adminRoute && store.canAdminister()) {\n <a\n class=\"icon-button\"\n [routerLink]=\"adminRoute\"\n [attr.aria-label]=\"'nh-assistant.panel.open-admin' | translate\"\n [attr.title]=\"'nh-assistant.panel.open-admin' | translate\"\n (click)=\"panel.close()\">\n <nh-assistant-icon name=\"admin\" />\n </a>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.new-conversation' | translate\"\n [attr.title]=\"'nh-assistant.panel.new-conversation' | translate\"\n (click)=\"newConversation()\">\n <nh-assistant-icon name=\"plus\" />\n </button>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.close' | translate\"\n [attr.title]=\"'nh-assistant.panel.close' | translate\"\n (click)=\"panel.close()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n </header>\n\n @if (store.enabled()) {\n <nh-assistant-activity [items]=\"store.activity()\" (conversationSelect)=\"openConversation($event)\" />\n }\n\n <div class=\"body\">\n @if (store.statusLoading() && !store.enabled()) {\n <div class=\"state\" role=\"status\">\n <p class=\"state-title\">{{ 'nh-assistant.panel.loading' | translate }}</p>\n </div>\n } @else if (!store.enabled()) {\n <div class=\"state\">\n <nh-assistant-icon class=\"state-icon\" name=\"warning\" />\n <p class=\"state-title\">{{ 'nh-assistant.panel.unavailable-title' | translate }}</p>\n <p>{{ 'nh-assistant.panel.unavailable-description' | translate }}</p>\n </div>\n } @else {\n @if (showPreferences()) {\n <nh-assistant-preferences class=\"overlay-view\" (closed)=\"showPreferences.set(false)\" />\n }\n\n @if (showShare()) {\n <nh-assistant-share class=\"overlay-view\" [section]=\"shareSection()\" (closed)=\"showShare.set(false)\" />\n }\n\n @if (showConversations()) {\n <nh-assistant-conversation-list\n class=\"overlay-view\"\n [conversations]=\"store.conversations()\"\n [total]=\"store.conversationsTotal()\"\n [activeConversationId]=\"conversation()?.id ?? null\"\n [loading]=\"store.conversationsLoading()\"\n (conversationSelect)=\"openConversation($event)\"\n (conversationDelete)=\"deleteConversation($event)\"\n (conversationCreate)=\"newConversation()\" />\n }\n\n @if (store.conversationLoading()) {\n <div class=\"state\" role=\"status\">\n <p class=\"state-title\">{{ 'nh-assistant.thread.loading' | translate }}</p>\n </div>\n } @else if (messages().length === 0 && !store.streaming()) {\n <div class=\"state\">\n <nh-assistant-icon class=\"state-icon\" name=\"assistant\" />\n <p class=\"state-title\">{{ 'nh-assistant.panel.empty-title' | translate }}</p>\n <p>{{ 'nh-assistant.panel.empty-description' | translate }}</p>\n </div>\n } @else {\n <nh-assistant-thread\n class=\"thread\"\n [attr.inert]=\"showPreferences() || showShare() || showConversations() ? '' : null\"\n [messages]=\"messages()\"\n [streaming]=\"busy()\"\n [deciding]=\"store.deciding()\"\n [markdown]=\"markdown\"\n [members]=\"store.members()\"\n [currentActorId]=\"conversation()?.currentActorId ?? null\"\n [canDecide]=\"store.canDecide()\"\n [decisionOwner]=\"activeMemberName()\"\n [approvalRights]=\"store.approvalRights()\"\n (approvalChoice)=\"decideApproval($event)\"\n (retryMessage)=\"store.send($event)\" />\n }\n }\n </div>\n\n @if (notificationError() ?? store.error(); as error) {\n <div class=\"error\" role=\"alert\">\n <nh-assistant-icon name=\"warning\" />\n <p>{{ errorKeys() | nhAssistantTranslate: error.code }}</p>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.dismiss-error' | translate\"\n [attr.title]=\"'nh-assistant.panel.dismiss-error' | translate\"\n (click)=\"store.clearError(); notificationError.set(null)\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n } @else if (store.notice(); as notice) {\n <div class=\"turn-notice\" role=\"status\">\n <nh-assistant-icon name=\"warning\" />\n <p>{{ noticeKeys() | nhAssistantTranslate: notice.code }}</p>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.dismiss-error' | translate\"\n [attr.title]=\"'nh-assistant.panel.dismiss-error' | translate\"\n (click)=\"store.clearNotice()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n }\n\n @if (store.enabled() && !showPreferences() && !showShare()) {\n <footer class=\"footer\" (focusin)=\"refreshPageContext()\">\n @if (closed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.conversation-closed' | translate }}</p>\n } @else if (lastTurnFailed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.last-turn-failed' | translate }}</p>\n }\n @if (store.collaboration() || store.pageContext()) {\n <div class=\"conversation-actions\">\n @if (store.collaboration()) {\n @if (!conversation() || store.isOwner()) {\n <button class=\"icon-button\" type=\"button\" [disabled]=\"!canShare()\"\n [attr.aria-label]=\"'nh-assistant.panel.share' | translate\"\n [attr.title]=\"'nh-assistant.panel.share' | translate\"\n [attr.aria-expanded]=\"showShare() && shareSection() === 'link'\"\n (click)=\"toggleShare('link')\">\n <nh-assistant-icon name=\"link\" />\n </button>\n }\n <button class=\"icon-button\" type=\"button\" [disabled]=\"!canShare()\"\n [class.active]=\"shared()\"\n [attr.aria-label]=\"(store.isOwner() || !conversation() ? 'nh-assistant.panel.add-members' : 'nh-assistant.panel.members') | translate\"\n [attr.title]=\"(store.isOwner() || !conversation() ? 'nh-assistant.panel.add-members' : 'nh-assistant.panel.members') | translate\"\n [attr.aria-expanded]=\"showShare() && shareSection() === 'members'\"\n (click)=\"toggleShare('members')\">\n <nh-assistant-icon name=\"users\" />\n </button>\n }\n @if (store.pageContext(); as context) {\n <button\n class=\"icon-button page-context-button\"\n [class.active]=\"!store.pageContextExcluded()\"\n type=\"button\"\n [attr.aria-pressed]=\"!store.pageContextExcluded()\"\n [attr.aria-label]=\"(store.pageContextExcluded() ? 'nh-assistant.page-context.include' : 'nh-assistant.page-context.exclude') | translate: { page: describeContext(context) }\"\n [attr.title]=\"(store.pageContextExcluded() ? 'nh-assistant.page-context.include' : 'nh-assistant.page-context.exclude') | translate: { page: describeContext(context) }\"\n (click)=\"store.setPageContextExcluded(!store.pageContextExcluded())\">\n <nh-assistant-icon name=\"page\" />\n </button>\n }\n </div>\n }\n @if (store.queuedMessages().length > 0) {\n <section class=\"message-queue\" [attr.aria-label]=\"'nh-assistant.queue.title' | translate: { count: store.queuedMessages().length }\">\n <div class=\"queue-heading\">\n <span aria-live=\"polite\">{{ 'nh-assistant.queue.title' | translate: { count: store.queuedMessages().length } }}</span>\n @if (store.queuePaused()) {\n <button type=\"button\" class=\"queue-resume\" [disabled]=\"editingQueuedId() !== null\" (click)=\"store.resumeQueue()\">\n {{ 'nh-assistant.queue.resume' | translate }}\n </button>\n }\n </div>\n <ol class=\"queue-items\">\n @for (item of store.queuedMessages(); track item.id) {\n <li class=\"queue-item\" [class.failed]=\"item.state === 'failed'\">\n @if (editingQueuedId() === item.id && item.state !== 'sending') {\n <div class=\"queue-edit\">\n <textarea rows=\"2\" [value]=\"queuedDraft()\" (input)=\"changeQueuedDraft($event)\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\"></textarea>\n <div class=\"queue-buttons\">\n <button type=\"button\" class=\"icon-button\" [disabled]=\"!validQueuedDraft()\"\n [attr.aria-label]=\"'nh-assistant.queue.save' | translate\" [attr.title]=\"'nh-assistant.queue.save' | translate\"\n (click)=\"saveQueuedMessage(item.id)\"><nh-assistant-icon name=\"check\" /></button>\n <button type=\"button\" class=\"icon-button\" [attr.aria-label]=\"'nh-assistant.queue.cancel-edit' | translate\"\n [attr.title]=\"'nh-assistant.queue.cancel-edit' | translate\"\n (click)=\"cancelQueueEdit()\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n </div>\n } @else {\n <div class=\"queue-message\">\n <p [title]=\"item.text\">{{ item.text }}</p>\n @if (item.files?.length) {\n <span class=\"queue-state queue-files\" data-queue-files>{{ 'nh-assistant.queue.files' | translate: { count: item.files!.length, names: fileNames(item.files!) } }}</span>\n }\n @if (item.state !== 'queued') {\n <span class=\"queue-state\">{{ 'nh-assistant.queue.state.' + item.state | translate }}</span>\n }\n </div>\n <div class=\"queue-buttons\">\n @if (store.canSteer() && item.state === 'queued') {\n <button type=\"button\" class=\"queue-steer\" [disabled]=\"editingQueuedId() !== null\"\n [attr.title]=\"'nh-assistant.queue.steer-hint' | translate\"\n (click)=\"store.steerQueuedMessage(item.id)\">{{ 'nh-assistant.queue.steer' | translate }}</button>\n }\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\" [attr.title]=\"'nh-assistant.queue.edit' | translate\"\n (click)=\"editQueuedMessage(item.id, item.text)\"><nh-assistant-icon name=\"edit\" /></button>\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.remove' | translate\" [attr.title]=\"'nh-assistant.queue.remove' | translate\"\n (click)=\"store.removeQueuedMessage(item.id)\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n }\n </li>\n }\n </ol>\n </section>\n }\n <nh-assistant-composer\n [disabled]=\"closed()\"\n [sendDisabled]=\"composerDisabled()\"\n [busy]=\"busy()\"\n [queueWhileBusy]=\"true\"\n [status]=\"composerStatus()\"\n [statusActor]=\"activeMemberName()\"\n [canStop]=\"canStop()\"\n [maxLength]=\"store.limits()?.maxMessageChars ?? null\"\n [restore]=\"store.restoredDraft()\"\n [uploads]=\"store.uploads()\"\n [attachments]=\"store.attachments()\"\n (restored)=\"store.consumeRestoredDraft()\"\n (send)=\"send($event)\"\n (attach)=\"attach($event)\"\n (removeAttachment)=\"store.removeAttachment($event)\"\n (cancel)=\"cancel()\">\n @if (store.agents().length > 1) {\n <nh-assistant-agent-picker\n nhAssistantComposerControls\n [compact]=\"true\"\n [agents]=\"store.agents()\"\n [selectedAgentId]=\"store.selectedAgentId()\"\n (agentChange)=\"store.selectAgent($event)\" />\n }\n </nh-assistant-composer>\n </footer>\n }\n </section>\n</ng-template>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}.drawer{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){.drawer{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:contents}.drawer{position:relative;display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:100%;border-left:1px solid var(--nha-border);color:var(--nha-text);background:var(--nha-background);box-shadow:var(--nha-shadow);font-family:var(--nha-font);font-size:14px}.drawer *,.drawer *:before,.drawer *:after{box-sizing:border-box}.header{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px 12px;border-bottom:1px solid var(--nha-border);padding:12px 12px 12px 16px}.heading{display:contents}h2{grid-row:1;min-width:0;margin:0;overflow:hidden;font-size:16px;font-weight:750;letter-spacing:-.01em;text-overflow:ellipsis;white-space:nowrap}.header-actions{display:flex;flex:none;grid-column:2;grid-row:1;gap:2px;min-width:0}.header-actions .icon-button{width:32px;height:32px}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:var(--nha-radius-control);color:var(--nha-text-muted);background:transparent;cursor:pointer;text-decoration:none}.icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.icon-button:disabled{cursor:not-allowed;opacity:.5}.icon-button.active{color:var(--nha-highlight);background:var(--nha-surface-strong)}.body{position:relative;display:flex;flex:1;flex-direction:column;min-height:0;background:var(--nha-background)}.thread{flex:1;min-height:0}.overlay-view{position:absolute;z-index:1;inset:0;border-bottom:1px solid var(--nha-border);background:var(--nha-background)}.state{display:flex;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:24px;color:var(--nha-text-muted);text-align:center}.state p{max-width:32ch;margin:0;font-size:13px}.state-icon{margin-bottom:8px;color:var(--nha-highlight);font-size:28px}.state .state-title{color:var(--nha-text);font-size:15px;font-weight:700}.error{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-danger);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-danger);background:var(--nha-danger-soft)}.error p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.turn-notice{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-warning);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-warning);background:var(--nha-warning-soft)}.turn-notice p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.footer{padding:12px 16px 14px}.conversation-actions{display:flex;align-items:center;gap:4px;margin:0 -4px 6px}.page-context-button{margin-left:auto}.message-queue{margin-bottom:10px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);background:var(--nha-background);overflow:hidden;font-size:12px}.queue-heading{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:8px 10px;color:var(--nha-text-muted)}.queue-items{max-height:180px;overflow-y:auto;margin:0;padding:0;list-style:none}.queue-item{display:flex;align-items:center;gap:8px;border-top:1px solid var(--nha-border);padding:6px 8px 6px 10px}.queue-item.failed{border-left:2px solid var(--nha-danger)}.queue-message{flex:1;min-width:0}.queue-message p{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere;margin:0}.queue-state{color:var(--nha-text-muted);font-size:11px}.queue-files{display:block;overflow-wrap:anywhere}.queue-buttons{display:flex;flex-shrink:0;align-items:center;gap:2px}.queue-buttons .icon-button{width:28px;height:30px}.queue-resume,.queue-steer{border:0;border-radius:6px;padding:6px;color:var(--nha-highlight);background:transparent;font:inherit;cursor:pointer}.queue-resume:hover,.queue-steer:hover{background:var(--nha-surface)}.queue-resume:focus-visible,.queue-steer:focus-visible{outline:2px solid var(--nha-focus)}.queue-resume:disabled,.queue-steer:disabled{cursor:not-allowed;opacity:.5}.queue-edit{width:100%}.queue-edit textarea{box-sizing:border-box;width:100%;border:1px solid var(--nha-border);border-radius:6px;padding:8px;color:var(--nha-text);background:var(--nha-surface);font:inherit;resize:vertical}.queue-edit .queue-buttons{justify-content:flex-end}.notification-button{position:relative}.notification-button.off:after{content:\"\";position:absolute;width:18px;height:2px;border-radius:1px;background:currentColor;transform:rotate(-45deg)}.notification-button.blocked{color:var(--nha-warning)}@media(max-width:359px){.header{gap:6px;padding-inline:12px}.header-actions .icon-button{width:28px;height:32px}}.notice{margin:0 0 8px;color:var(--nha-text-muted);font-size:12px}\n"] }]
12181
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-template #drawer>\n <section class=\"drawer\" role=\"dialog\" aria-modal=\"false\" [attr.aria-labelledby]=\"titleId\">\n <header class=\"header\">\n <div class=\"heading\">\n <h2 [id]=\"titleId\">{{ 'nh-assistant.panel.title' | translate }}</h2>\n </div>\n <div class=\"header-actions\">\n @if (store.enabled()) {\n @if (push.state() !== 'unavailable') {\n <button\n class=\"icon-button notification-button\"\n type=\"button\"\n [class.active]=\"push.state() === 'on'\"\n [class.off]=\"!push.settings()?.pushEnabled\"\n [class.blocked]=\"push.state() === 'blocked'\"\n [disabled]=\"push.busy()\"\n [attr.aria-pressed]=\"push.settings()?.pushEnabled === true\"\n [attr.aria-label]=\"notificationsLabel() | translate\"\n [attr.title]=\"('nh-assistant.preferences.notifications-state.' + push.state() | translate) + ' \u00B7 ' + (notificationsLabel() | translate)\"\n (click)=\"toggleNotifications()\">\n <nh-assistant-icon name=\"bell\" />\n </button>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [class.active]=\"showConversations()\"\n [attr.aria-expanded]=\"showConversations()\"\n [attr.aria-label]=\"(showConversations() ? 'nh-assistant.panel.hide-conversations' : 'nh-assistant.panel.show-conversations') | translate\"\n [attr.title]=\"(showConversations() ? 'nh-assistant.panel.hide-conversations' : 'nh-assistant.panel.show-conversations') | translate\"\n (click)=\"toggleConversations()\">\n <nh-assistant-icon name=\"list\" />\n </button>\n <button\n class=\"icon-button\"\n type=\"button\"\n [class.active]=\"showPreferences()\"\n [attr.aria-expanded]=\"showPreferences()\"\n [attr.aria-label]=\"'nh-assistant.panel.open-preferences' | translate\"\n [attr.title]=\"'nh-assistant.panel.open-preferences' | translate\"\n (click)=\"togglePreferences()\">\n <nh-assistant-icon name=\"settings\" />\n </button>\n @if (adminRoute && store.canAdminister()) {\n <a\n class=\"icon-button\"\n [routerLink]=\"adminRoute\"\n [attr.aria-label]=\"'nh-assistant.panel.open-admin' | translate\"\n [attr.title]=\"'nh-assistant.panel.open-admin' | translate\"\n (click)=\"panel.close()\">\n <nh-assistant-icon name=\"admin\" />\n </a>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.new-conversation' | translate\"\n [attr.title]=\"'nh-assistant.panel.new-conversation' | translate\"\n (click)=\"newConversation()\">\n <nh-assistant-icon name=\"plus\" />\n </button>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.close' | translate\"\n [attr.title]=\"'nh-assistant.panel.close' | translate\"\n (click)=\"panel.close()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n </header>\n\n @if (store.enabled()) {\n <nh-assistant-activity [items]=\"store.activity()\" (conversationSelect)=\"openConversation($event)\" />\n }\n\n <div class=\"body\">\n @if (store.statusLoading() && !store.enabled()) {\n <div class=\"state\" role=\"status\">\n <p class=\"state-title\">{{ 'nh-assistant.panel.loading' | translate }}</p>\n </div>\n } @else if (!store.enabled()) {\n <div class=\"state\">\n <nh-assistant-icon class=\"state-icon\" name=\"warning\" />\n <p class=\"state-title\">{{ 'nh-assistant.panel.unavailable-title' | translate }}</p>\n <p>{{ 'nh-assistant.panel.unavailable-description' | translate }}</p>\n </div>\n } @else {\n @if (showPreferences()) {\n <nh-assistant-preferences class=\"overlay-view\" (closed)=\"showPreferences.set(false)\" />\n }\n\n @if (showShare()) {\n <nh-assistant-share class=\"overlay-view\" [section]=\"shareSection()\" (closed)=\"showShare.set(false)\" />\n }\n\n @if (showConversations()) {\n <nh-assistant-conversation-list\n class=\"overlay-view\"\n [conversations]=\"store.conversations()\"\n [total]=\"store.conversationsTotal()\"\n [activeConversationId]=\"conversation()?.id ?? null\"\n [loading]=\"store.conversationsLoading()\"\n (conversationSelect)=\"openConversation($event)\"\n (conversationDelete)=\"deleteConversation($event)\"\n (conversationCreate)=\"newConversation()\" />\n }\n\n @if (store.conversationLoading()) {\n <div class=\"state\" role=\"status\">\n <p class=\"state-title\">{{ 'nh-assistant.thread.loading' | translate }}</p>\n </div>\n } @else if (messages().length === 0 && !store.streaming()) {\n <div class=\"state\">\n <nh-assistant-icon class=\"state-icon\" name=\"assistant\" />\n <p class=\"state-title\">{{ 'nh-assistant.panel.empty-title' | translate }}</p>\n <p>{{ 'nh-assistant.panel.empty-description' | translate }}</p>\n </div>\n } @else {\n <nh-assistant-thread\n class=\"thread\"\n [attr.inert]=\"showPreferences() || showShare() || showConversations() ? '' : null\"\n [messages]=\"messages()\"\n [streaming]=\"busy()\"\n [deciding]=\"store.deciding()\"\n [markdown]=\"markdown\"\n [members]=\"store.members()\"\n [currentActorId]=\"conversation()?.currentActorId ?? null\"\n [canDecide]=\"store.canDecide()\"\n [decisionOwner]=\"activeMemberName()\"\n [approvalRights]=\"store.approvalRights()\"\n [writingMessageId]=\"store.writingMessageId()\"\n (approvalChoice)=\"decideApproval($event)\"\n (retryMessage)=\"store.send($event)\" />\n }\n }\n </div>\n\n @if (notificationError() ?? store.error(); as error) {\n <div class=\"error\" role=\"alert\">\n <nh-assistant-icon name=\"warning\" />\n <p>{{ errorKeys() | nhAssistantTranslate: error.code }}</p>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.dismiss-error' | translate\"\n [attr.title]=\"'nh-assistant.panel.dismiss-error' | translate\"\n (click)=\"store.clearError(); notificationError.set(null)\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n } @else if (store.notice(); as notice) {\n <div class=\"turn-notice\" role=\"status\">\n <nh-assistant-icon name=\"warning\" />\n <p>{{ noticeKeys() | nhAssistantTranslate: notice.code }}</p>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.dismiss-error' | translate\"\n [attr.title]=\"'nh-assistant.panel.dismiss-error' | translate\"\n (click)=\"store.clearNotice()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n }\n\n @if (store.enabled() && !showPreferences() && !showShare()) {\n <footer class=\"footer\" (focusin)=\"refreshPageContext()\">\n @if (closed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.conversation-closed' | translate }}</p>\n } @else if (lastTurnFailed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.last-turn-failed' | translate }}</p>\n }\n @if (store.collaboration() || store.pageContext()) {\n <div class=\"conversation-actions\">\n @if (store.collaboration()) {\n @if (!conversation() || store.isOwner()) {\n <button class=\"icon-button\" type=\"button\" [disabled]=\"!canShare()\"\n [attr.aria-label]=\"'nh-assistant.panel.share' | translate\"\n [attr.title]=\"'nh-assistant.panel.share' | translate\"\n [attr.aria-expanded]=\"showShare() && shareSection() === 'link'\"\n (click)=\"toggleShare('link')\">\n <nh-assistant-icon name=\"link\" />\n </button>\n }\n <button class=\"icon-button\" type=\"button\" [disabled]=\"!canShare()\"\n [class.active]=\"shared()\"\n [attr.aria-label]=\"(store.isOwner() || !conversation() ? 'nh-assistant.panel.add-members' : 'nh-assistant.panel.members') | translate\"\n [attr.title]=\"(store.isOwner() || !conversation() ? 'nh-assistant.panel.add-members' : 'nh-assistant.panel.members') | translate\"\n [attr.aria-expanded]=\"showShare() && shareSection() === 'members'\"\n (click)=\"toggleShare('members')\">\n <nh-assistant-icon name=\"users\" />\n </button>\n }\n @if (store.pageContext(); as context) {\n <button\n class=\"icon-button page-context-button\"\n [class.active]=\"!store.pageContextExcluded()\"\n type=\"button\"\n [attr.aria-pressed]=\"!store.pageContextExcluded()\"\n [attr.aria-label]=\"(store.pageContextExcluded() ? 'nh-assistant.page-context.include' : 'nh-assistant.page-context.exclude') | translate: { page: describeContext(context) }\"\n [attr.title]=\"(store.pageContextExcluded() ? 'nh-assistant.page-context.include' : 'nh-assistant.page-context.exclude') | translate: { page: describeContext(context) }\"\n (click)=\"store.setPageContextExcluded(!store.pageContextExcluded())\">\n <nh-assistant-icon name=\"page\" />\n </button>\n }\n </div>\n }\n @if (store.queuedMessages().length > 0) {\n <section class=\"message-queue\" [attr.aria-label]=\"'nh-assistant.queue.title' | translate: { count: store.queuedMessages().length }\">\n <div class=\"queue-heading\">\n <span aria-live=\"polite\">{{ 'nh-assistant.queue.title' | translate: { count: store.queuedMessages().length } }}</span>\n @if (store.queuePaused()) {\n <button type=\"button\" class=\"queue-resume\" [disabled]=\"editingQueuedId() !== null\" (click)=\"store.resumeQueue()\">\n {{ 'nh-assistant.queue.resume' | translate }}\n </button>\n }\n </div>\n <ol class=\"queue-items\">\n @for (item of store.queuedMessages(); track item.id) {\n <li class=\"queue-item\" [class.failed]=\"item.state === 'failed'\">\n @if (editingQueuedId() === item.id && item.state !== 'sending') {\n <div class=\"queue-edit\">\n <textarea rows=\"2\" [value]=\"queuedDraft()\" (input)=\"changeQueuedDraft($event)\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\"></textarea>\n <div class=\"queue-buttons\">\n <button type=\"button\" class=\"icon-button\" [disabled]=\"!validQueuedDraft()\"\n [attr.aria-label]=\"'nh-assistant.queue.save' | translate\" [attr.title]=\"'nh-assistant.queue.save' | translate\"\n (click)=\"saveQueuedMessage(item.id)\"><nh-assistant-icon name=\"check\" /></button>\n <button type=\"button\" class=\"icon-button\" [attr.aria-label]=\"'nh-assistant.queue.cancel-edit' | translate\"\n [attr.title]=\"'nh-assistant.queue.cancel-edit' | translate\"\n (click)=\"cancelQueueEdit()\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n </div>\n } @else {\n <div class=\"queue-message\">\n <p [title]=\"item.text\">{{ item.text }}</p>\n @if (item.files?.length) {\n <span class=\"queue-state queue-files\" data-queue-files>{{ 'nh-assistant.queue.files' | translate: { count: item.files!.length, names: fileNames(item.files!) } }}</span>\n }\n @if (item.state !== 'queued') {\n <span class=\"queue-state\">{{ 'nh-assistant.queue.state.' + item.state | translate }}</span>\n }\n </div>\n <div class=\"queue-buttons\">\n @if (store.canSteer() && item.state === 'queued') {\n <button type=\"button\" class=\"queue-steer\" [disabled]=\"editingQueuedId() !== null\"\n [attr.title]=\"'nh-assistant.queue.steer-hint' | translate\"\n (click)=\"store.steerQueuedMessage(item.id)\">{{ 'nh-assistant.queue.steer' | translate }}</button>\n }\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\" [attr.title]=\"'nh-assistant.queue.edit' | translate\"\n (click)=\"editQueuedMessage(item.id, item.text)\"><nh-assistant-icon name=\"edit\" /></button>\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.remove' | translate\" [attr.title]=\"'nh-assistant.queue.remove' | translate\"\n (click)=\"store.removeQueuedMessage(item.id)\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n }\n </li>\n }\n </ol>\n </section>\n }\n <nh-assistant-composer\n [disabled]=\"closed()\"\n [sendDisabled]=\"composerDisabled()\"\n [busy]=\"busy()\"\n [queueWhileBusy]=\"true\"\n [status]=\"composerStatus()\"\n [statusActor]=\"activeMemberName()\"\n [canStop]=\"canStop()\"\n [maxLength]=\"store.limits()?.maxMessageChars ?? null\"\n [restore]=\"store.restoredDraft()\"\n [uploads]=\"store.uploads()\"\n [attachments]=\"store.attachments()\"\n (restored)=\"store.consumeRestoredDraft()\"\n (send)=\"send($event)\"\n (attach)=\"attach($event)\"\n (removeAttachment)=\"store.removeAttachment($event)\"\n (cancel)=\"cancel()\">\n @if (store.agents().length > 1) {\n <nh-assistant-agent-picker\n nhAssistantComposerControls\n [compact]=\"true\"\n [agents]=\"store.agents()\"\n [selectedAgentId]=\"store.selectedAgentId()\"\n (agentChange)=\"store.selectAgent($event)\" />\n }\n </nh-assistant-composer>\n </footer>\n }\n </section>\n</ng-template>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}.drawer{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){.drawer{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:contents}.drawer{position:relative;display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:100%;border-left:1px solid var(--nha-border);color:var(--nha-text);background:var(--nha-background);box-shadow:var(--nha-shadow);font-family:var(--nha-font);font-size:14px}.drawer *,.drawer *:before,.drawer *:after{box-sizing:border-box}.header{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px 12px;border-bottom:1px solid var(--nha-border);padding:12px 12px 12px 16px}.heading{display:contents}h2{grid-row:1;min-width:0;margin:0;overflow:hidden;font-size:16px;font-weight:750;letter-spacing:-.01em;text-overflow:ellipsis;white-space:nowrap}.header-actions{display:flex;flex:none;grid-column:2;grid-row:1;gap:2px;min-width:0}.header-actions .icon-button{width:32px;height:32px}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:var(--nha-radius-control);color:var(--nha-text-muted);background:transparent;cursor:pointer;text-decoration:none}.icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.icon-button:disabled{cursor:not-allowed;opacity:.5}.icon-button.active{color:var(--nha-highlight);background:var(--nha-surface-strong)}.body{position:relative;display:flex;flex:1;flex-direction:column;min-height:0;background:var(--nha-background)}.thread{flex:1;min-height:0}.overlay-view{position:absolute;z-index:1;inset:0;border-bottom:1px solid var(--nha-border);background:var(--nha-background)}.state{display:flex;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:24px;color:var(--nha-text-muted);text-align:center}.state p{max-width:32ch;margin:0;font-size:13px}.state-icon{margin-bottom:8px;color:var(--nha-highlight);font-size:28px}.state .state-title{color:var(--nha-text);font-size:15px;font-weight:700}.error{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-danger);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-danger);background:var(--nha-danger-soft)}.error p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.turn-notice{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-warning);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-warning);background:var(--nha-warning-soft)}.turn-notice p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.footer{padding:12px 16px 14px}.conversation-actions{display:flex;align-items:center;gap:4px;margin:0 -4px 6px}.page-context-button{margin-left:auto}.message-queue{margin-bottom:10px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);background:var(--nha-background);overflow:hidden;font-size:12px}.queue-heading{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:8px 10px;color:var(--nha-text-muted)}.queue-items{max-height:180px;overflow-y:auto;margin:0;padding:0;list-style:none}.queue-item{display:flex;align-items:center;gap:8px;border-top:1px solid var(--nha-border);padding:6px 8px 6px 10px}.queue-item.failed{border-left:2px solid var(--nha-danger)}.queue-message{flex:1;min-width:0}.queue-message p{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere;margin:0}.queue-state{color:var(--nha-text-muted);font-size:11px}.queue-files{display:block;overflow-wrap:anywhere}.queue-buttons{display:flex;flex-shrink:0;align-items:center;gap:2px}.queue-buttons .icon-button{width:28px;height:30px}.queue-resume,.queue-steer{border:0;border-radius:6px;padding:6px;color:var(--nha-highlight);background:transparent;font:inherit;cursor:pointer}.queue-resume:hover,.queue-steer:hover{background:var(--nha-surface)}.queue-resume:focus-visible,.queue-steer:focus-visible{outline:2px solid var(--nha-focus)}.queue-resume:disabled,.queue-steer:disabled{cursor:not-allowed;opacity:.5}.queue-edit{width:100%}.queue-edit textarea{box-sizing:border-box;width:100%;border:1px solid var(--nha-border);border-radius:6px;padding:8px;color:var(--nha-text);background:var(--nha-surface);font:inherit;resize:vertical}.queue-edit .queue-buttons{justify-content:flex-end}.notification-button{position:relative}.notification-button.off:after{content:\"\";position:absolute;width:18px;height:2px;border-radius:1px;background:currentColor;transform:rotate(-45deg)}.notification-button.blocked{color:var(--nha-warning)}@media(max-width:359px){.header{gap:6px;padding-inline:12px}.header-actions .icon-button{width:28px;height:32px}}.notice{margin:0 0 8px;color:var(--nha-text-muted);font-size:12px}\n"] }]
11199
12182
  }], ctorParameters: () => [], propDecorators: { drawer: [{ type: i0.ViewChild, args: ['drawer', { isSignal: true }] }] } });
11200
12183
 
11201
12184
  /**
@@ -11520,102 +12503,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
11520
12503
  args: [{ selector: 'nh-assistant-picker', standalone: true, imports: [TranslatePipe, NhAssistantTranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"picker\" [class.open]=\"open()\">\n <div class=\"control\">\n <input\n #field\n type=\"text\"\n role=\"combobox\"\n autocomplete=\"off\"\n aria-autocomplete=\"list\"\n [id]=\"inputId()\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listId\"\n [attr.aria-activedescendant]=\"activeId()\"\n [attr.aria-invalid]=\"invalid() ? true : null\"\n [attr.aria-describedby]=\"fieldDescribedBy()\"\n [attr.placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [value]=\"inputText()\"\n (input)=\"onInput($event)\"\n (click)=\"openList()\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\" />\n <button\n type=\"button\"\n class=\"toggle\"\n tabindex=\"-1\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.picker.toggle' | translate\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"toggle()\">\n <nh-assistant-icon name=\"chevron-down\" />\n </button>\n </div>\n @if (searchError(); as failure) {\n <!-- A failed search says why and offers to search again; the list never claims nothing was found. -->\n <p class=\"field-error search-error\" role=\"alert\" data-search-error [id]=\"searchErrorId\">\n <nh-assistant-icon name=\"warning\" />\n <span>{{ searchErrorKeys() | nhAssistantTranslate: failure.code }}</span>\n <button\n type=\"button\"\n class=\"link\"\n data-search-retry\n [disabled]=\"disabled()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"retrySearch()\">{{ 'nh-assistant.picker.search-again' | translate }}</button>\n </p>\n }\n @if (open()) {\n <ul class=\"listbox\" role=\"listbox\" [id]=\"listId\">\n @for (option of visible(); track option.value; let index = $index) {\n <li\n role=\"option\"\n [id]=\"optionId(index)\"\n [class.active]=\"index === active()\"\n [attr.aria-selected]=\"option.value === (value() ?? '')\"\n (mousedown)=\"$event.preventDefault()\"\n (mousemove)=\"active.set(index)\"\n (click)=\"choose(option)\">\n <span class=\"label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"description\">{{ option.description }}</span>\n }\n </li>\n } @empty {\n <li class=\"state\" role=\"presentation\">{{ emptyKey() | translate }}</li>\n }\n </ul>\n } @else if (selected()?.description; as description) {\n <p class=\"chosen\">{{ description }}</p>\n }\n</div>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:block;min-width:0;color:var(--nha-text);font-family:var(--nha-font)}:host fieldset{min-width:0;margin:0;border:0;padding:0}:host legend,:host .label,:host .field>label{display:block;margin-bottom:6px;padding:0;color:var(--nha-text);font-size:12px;font-weight:700}:host input[type=text],:host input[type=url],:host input[type=password],:host input[type=search],:host input[type=email],:host input[type=number],:host input[type=date],:host input[type=time],:host input[type=datetime-local],:host select,:host textarea{box-sizing:border-box;width:100%;min-width:0;min-height:38px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:8px 10px;color:var(--nha-text);background:var(--nha-background);font:inherit;font-size:13px}:host input[type=text]:focus-visible,:host input[type=url]:focus-visible,:host input[type=password]:focus-visible,:host input[type=search]:focus-visible,:host input[type=email]:focus-visible,:host input[type=number]:focus-visible,:host input[type=date]:focus-visible,:host input[type=time]:focus-visible,:host input[type=datetime-local]:focus-visible,:host select:focus-visible,:host textarea:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host input[type=text]:disabled,:host input[type=url]:disabled,:host input[type=password]:disabled,:host input[type=search]:disabled,:host input[type=email]:disabled,:host input[type=number]:disabled,:host input[type=date]:disabled,:host input[type=time]:disabled,:host input[type=datetime-local]:disabled,:host select:disabled,:host textarea:disabled{cursor:not-allowed;opacity:.6}:host input[type=text][aria-invalid=true],:host input[type=url][aria-invalid=true],:host input[type=password][aria-invalid=true],:host input[type=search][aria-invalid=true],:host input[type=email][aria-invalid=true],:host input[type=number][aria-invalid=true],:host input[type=date][aria-invalid=true],:host input[type=time][aria-invalid=true],:host input[type=datetime-local][aria-invalid=true],:host select[aria-invalid=true],:host textarea[aria-invalid=true]{border-color:var(--nha-danger)}:host textarea{line-height:1.45;resize:vertical}:host input[type=number]{font-variant-numeric:tabular-nums}:host .mono{font-family:var(--nha-mono);font-size:12px}:host .field{display:flex;flex-direction:column;min-width:0}:host .hint{display:flex;justify-content:space-between;gap:8px;margin:4px 2px 0;color:var(--nha-text-muted);font-size:11px}:host .counter{flex:none;font-variant-numeric:tabular-nums}:host .error-text{color:var(--nha-danger)}:host .field-error{display:flex;align-items:flex-start;gap:6px;margin:4px 2px 0;color:var(--nha-danger);font-size:12px;font-weight:600;overflow-wrap:anywhere}:host .field-error nh-assistant-icon{flex:none;margin-top:1px}:host .form-error{box-sizing:border-box;display:flex;align-items:flex-start;gap:8px;margin:0;border:1px solid var(--nha-danger);border-radius:var(--nha-radius-control);padding:8px 12px;color:var(--nha-text);background:var(--nha-danger-soft);font-size:13px;overflow-wrap:anywhere}:host .form-error ul{margin:4px 0 0;padding-left:18px}:host .message{box-sizing:border-box;display:flex;align-items:center;gap:8px;margin:0;border:1px solid transparent;border-radius:var(--nha-radius-control);padding:8px 12px;font-size:13px;overflow-wrap:anywhere}:host .message.error{border-color:var(--nha-danger);color:var(--nha-text);background:var(--nha-danger-soft)}:host .message.warning{border-color:var(--nha-warning);color:var(--nha-text);background:var(--nha-warning-soft)}:host .message.success{border-color:var(--nha-success);color:var(--nha-text);background:var(--nha-success-soft)}:host .message.info{border-color:var(--nha-border);color:var(--nha-text);background:var(--nha-surface)}:host .message nh-assistant-icon{flex:none}:host button.link{min-height:0;border:0;padding:0;color:var(--nha-accent, currentColor);background:none;font:inherit;text-decoration:underline;cursor:pointer}:host button.link:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host button.link:disabled{cursor:not-allowed;opacity:.55}:host .primary,:host .secondary,:host .danger{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;gap:6px;min-height:36px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 14px;font:inherit;font-size:13px;font-weight:700;white-space:nowrap;cursor:pointer}:host .primary:focus-visible,:host .secondary:focus-visible,:host .danger:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host .primary:disabled,:host .secondary:disabled,:host .danger:disabled{cursor:not-allowed;opacity:.55}:host .primary{border-color:var(--nha-accent);color:var(--nha-accent-contrast);background:var(--nha-accent)}:host .primary:hover:not(:disabled){filter:brightness(1.12)}:host .secondary{color:var(--nha-text);background:transparent}:host .secondary:hover:not(:disabled){background:var(--nha-surface-strong)}:host .danger{border-color:var(--nha-danger);color:var(--nha-danger);background:transparent}:host .danger:hover:not(:disabled){background:var(--nha-danger-soft)}:host .icon-button{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:var(--nha-radius-control);color:var(--nha-text-muted);background:transparent;cursor:pointer}:host .icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host .icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}:host .icon-button:disabled{cursor:not-allowed;opacity:.5}:host .actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px}:host .badge{display:inline-flex;align-items:center;gap:4px;border-radius:999px;padding:2px 8px;color:var(--nha-text-muted);background:var(--nha-surface-strong);font-size:11px;font-weight:700;white-space:nowrap}:host .badge.success{color:var(--nha-success);background:var(--nha-success-soft)}:host .badge.warning{color:var(--nha-warning);background:var(--nha-warning-soft)}:host .badge.danger{color:var(--nha-danger);background:var(--nha-danger-soft)}:host .visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.picker{position:relative;min-width:0}.control{position:relative}.control input{padding-right:36px;text-overflow:ellipsis}.toggle{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:var(--nha-radius-control);background:transparent;cursor:pointer;position:absolute;top:50%;right:4px;width:30px;height:30px;transform:translateY(-50%);color:var(--nha-text-muted)}.toggle:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.toggle:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.toggle:disabled{cursor:not-allowed;opacity:.5}.open .toggle{transform:translateY(-50%) rotate(180deg)}.listbox{position:absolute;z-index:30;top:calc(100% + 4px);right:0;left:0;max-height:280px;margin:0;overflow-y:auto;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:4px;background:var(--nha-background);box-shadow:0 12px 28px #0000002e;list-style:none}[role=option]{display:grid;gap:2px;border-radius:8px;padding:7px 10px;cursor:pointer;overflow-wrap:anywhere}[role=option].active{background:var(--nha-surface-strong)}[role=option][aria-selected=true] .label{font-weight:700}.label{font-size:13px}.description,.chosen{color:var(--nha-text-muted);font-size:12px;line-height:1.4}.chosen{margin:4px 2px 0;overflow-wrap:anywhere}.state{padding:8px 10px;color:var(--nha-text-muted);font-size:12px}.search-error{flex-wrap:wrap;align-items:baseline}.search-error span{flex:1 1 auto;min-width:0}\n"] }]
11521
12504
  }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.ViewChild, args: ['field', { isSignal: true }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], selectedLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedLabel", required: false }] }], noneLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "noneLabel", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], search: [{ type: i0.Input, args: [{ isSignal: true, alias: "search", required: false }] }], minQueryLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minQueryLength", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], describedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "describedBy", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], optionChange: [{ type: i0.Output, args: ["optionChange"] }] } });
11522
12505
 
11523
- /**
11524
- * The name of a subject with the name of its type, linked to where the application shows the
11525
- * subject (internal). The host's `subjectLinks` builder wins over the server's link; a link within
11526
- * the application navigates through the router, an `https` link leaves it, and anything else shows
11527
- * no link. Ctrl-, Cmd-, Shift- and middle-clicks keep the browser's behavior, such as a new tab.
11528
- * A name that already says the type, as "The whole application" does for the application as a
11529
- * whole, is shown once.
11530
- */
11531
- class NhAssistantSubjectLinkComponent {
11532
- constructor() {
11533
- this.config = inject(NH_ASSISTANT_CONFIG, { optional: true });
11534
- this.router = inject(Router, { optional: true });
11535
- this.locationStrategy = inject(LocationStrategy, { optional: true });
11536
- this.injector = inject(EnvironmentInjector);
11537
- this.subjectType = input.required(...(ngDevMode ? [{ debugName: "subjectType" }] : []));
11538
- this.subjectId = input.required(...(ngDevMode ? [{ debugName: "subjectId" }] : []));
11539
- /** The text people read, usually the subject's display name. */
11540
- this.label = input.required(...(ngDevMode ? [{ debugName: "label" }] : []));
11541
- /** The name of the subject type, such as "Project"; nothing is shown without one. */
11542
- this.typeName = input(null, ...(ngDevMode ? [{ debugName: "typeName" }] : []));
11543
- /** The server's link to the subject. */
11544
- this.link = input(null, ...(ngDevMode ? [{ debugName: "link" }] : []));
11545
- /** The type's name, unless the subject's name is the type's name itself, as for the whole application. */
11546
- this.shownTypeName = computed(() => {
11547
- const type = this.typeName()?.trim();
11548
- if (!type) {
11549
- return null;
11550
- }
11551
- return type.toLocaleLowerCase() === this.label().trim().toLocaleLowerCase() ? null : type;
11552
- }, ...(ngDevMode ? [{ debugName: "shownTypeName" }] : []));
11553
- this.target = computed(() => {
11554
- const builder = this.config?.subjectLinks?.[this.subjectType()];
11555
- const subject = { type: this.subjectType(), id: this.subjectId() };
11556
- const custom = builder ? runInInjectionContext(this.injector, () => builder(subject)) : undefined;
11557
- if (custom === null) {
11558
- return null;
11559
- }
11560
- if (Array.isArray(custom)) {
11561
- return this.routeTarget(this.router ? this.router.serializeUrl(this.router.createUrlTree([...custom])) : null);
11562
- }
11563
- return this.urlTarget(typeof custom === 'string' ? custom : this.link() ?? null);
11564
- }, ...(ngDevMode ? [{ debugName: "target" }] : []));
11565
- }
11566
- open(event, target) {
11567
- if (!target.route || !this.router || event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) {
11568
- return;
11569
- }
11570
- event.preventDefault();
11571
- void this.router.navigateByUrl(target.route);
11572
- }
11573
- urlTarget(url) {
11574
- if (!url) {
11575
- return null;
11576
- }
11577
- if (url.startsWith('/') && !url.startsWith('//') && !url.startsWith('/\\')) {
11578
- return this.routeTarget(url);
11579
- }
11580
- return /^https:\/\/[^/\\\s]+/i.test(url) ? { href: url, route: null } : null;
11581
- }
11582
- routeTarget(url) {
11583
- if (!url) {
11584
- return null;
11585
- }
11586
- return {
11587
- href: this.locationStrategy ? this.locationStrategy.prepareExternalUrl(url) : url,
11588
- route: this.router ? url : null
11589
- };
11590
- }
11591
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantSubjectLinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
11592
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", type: NhAssistantSubjectLinkComponent, isStandalone: true, selector: "nh-assistant-subject-link", inputs: { subjectType: { classPropertyName: "subjectType", publicName: "subjectType", isSignal: true, isRequired: true, transformFunction: null }, subjectId: { classPropertyName: "subjectId", publicName: "subjectId", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, typeName: { classPropertyName: "typeName", publicName: "typeName", isSignal: true, isRequired: false, transformFunction: null }, link: { classPropertyName: "link", publicName: "link", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
11593
- @if (shownTypeName(); as type) {
11594
- <!-- The space keeps the type and the name apart for screen readers; the flex gap shows it. -->
11595
- <span class="nh-assistant-subject-type">{{ type }}</span>{{ ' ' }}
11596
- }
11597
- @if (target(); as link) {
11598
- <a class="nh-assistant-subject-name" [href]="link.href" (click)="open($event, link)">{{ label() }}</a>
11599
- } @else {
11600
- <span class="nh-assistant-subject-name">{{ label() }}</span>
11601
- }
11602
- `, isInline: true, styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:inline-flex;flex-wrap:wrap;align-items:baseline;gap:.35em;min-width:0;overflow-wrap:anywhere}.nh-assistant-subject-type{color:var(--nha-text-muted);font-size:.85em;font-weight:500}a.nh-assistant-subject-name{border-radius:2px;color:inherit;text-decoration:underline;text-underline-offset:.15em}a.nh-assistant-subject-name:hover{color:var(--nha-accent)}a.nh-assistant-subject-name:focus-visible{outline:2px solid var(--nha-focus);outline-offset:2px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11603
- }
11604
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantSubjectLinkComponent, decorators: [{
11605
- type: Component,
11606
- args: [{ selector: 'nh-assistant-subject-link', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
11607
- @if (shownTypeName(); as type) {
11608
- <!-- The space keeps the type and the name apart for screen readers; the flex gap shows it. -->
11609
- <span class="nh-assistant-subject-type">{{ type }}</span>{{ ' ' }}
11610
- }
11611
- @if (target(); as link) {
11612
- <a class="nh-assistant-subject-name" [href]="link.href" (click)="open($event, link)">{{ label() }}</a>
11613
- } @else {
11614
- <span class="nh-assistant-subject-name">{{ label() }}</span>
11615
- }
11616
- `, styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:inline-flex;flex-wrap:wrap;align-items:baseline;gap:.35em;min-width:0;overflow-wrap:anywhere}.nh-assistant-subject-type{color:var(--nha-text-muted);font-size:.85em;font-weight:500}a.nh-assistant-subject-name{border-radius:2px;color:inherit;text-decoration:underline;text-underline-offset:.15em}a.nh-assistant-subject-name:hover{color:var(--nha-accent)}a.nh-assistant-subject-name:focus-visible{outline:2px solid var(--nha-focus);outline-offset:2px}\n"] }]
11617
- }], propDecorators: { subjectType: [{ type: i0.Input, args: [{ isSignal: true, alias: "subjectType", required: true }] }], subjectId: [{ type: i0.Input, args: [{ isSignal: true, alias: "subjectId", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], typeName: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeName", required: false }] }], link: [{ type: i0.Input, args: [{ isSignal: true, alias: "link", required: false }] }] } });
11618
-
11619
12506
  /*
11620
12507
  * Public API surface of @newheap/platform-ai-chat.
11621
12508
  */
@@ -11624,5 +12511,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
11624
12511
  * Generated bundle index. Do not edit.
11625
12512
  */
11626
12513
 
11627
- export { NH_ASSISTANT_ACCESS_POLICY, NH_ASSISTANT_CONFIG, NH_ASSISTANT_CONVERSATION_FRAGMENT, NH_ASSISTANT_ERROR_CODES, NH_ASSISTANT_FEEDBACK_CATEGORIES, NH_ASSISTANT_FEEDBACK_GROUPS, NH_ASSISTANT_FETCH, NH_ASSISTANT_ICONS, NH_ASSISTANT_JOIN_FRAGMENT, NH_ASSISTANT_MAX_BULK_FEEDBACK, NH_ASSISTANT_MAX_CUSTOM_INSTRUCTIONS, NH_ASSISTANT_MAX_FEEDBACK_TEXT, NH_ASSISTANT_MAX_PROPOSAL_LOOKUP, NH_ASSISTANT_MAX_SUBJECT_SUMMARY, NH_ASSISTANT_MCP_AUTHORIZATION_MESSAGE, NH_ASSISTANT_MCP_AUTHORIZATION_WINDOW, NH_ASSISTANT_MESSAGE_QUEUED_NOTICE_CODE, NH_ASSISTANT_NO_ANSWER_NOTICE_CODE, NH_ASSISTANT_PAGE_CONTEXT_LIMITS, NH_ASSISTANT_PANEL_MOBILE_QUERY, NH_ASSISTANT_PANEL_WIDTH, NH_ASSISTANT_RESULT_PREVIEW_LENGTH, NH_ASSISTANT_RUN_HISTORY_MAX_PAGE, NH_ASSISTANT_RUN_RESULT_MAX_LENGTH, NH_ASSISTANT_SERVER_ERROR_CODES, NH_ASSISTANT_SSE_EVENT_TYPES, NH_ASSISTANT_SUBJECT_SEARCH, NH_ASSISTANT_SYSTEM_GLOBAL_ID, NH_ASSISTANT_SYSTEM_SUBJECT_TYPE, NH_ASSISTANT_TRANSLATIONS, NH_ASSISTANT_UPLOAD, NH_ASSISTANT_UPLOAD_HEADER, NH_ASSISTANT_VIRTUAL_SCROLL_THRESHOLD, NhAssistantActivityComponent, NhAssistantAdminApiService, NhAssistantAgentPickerComponent, NhAssistantAgentRunApiService, NhAssistantAllowAllAccessPolicy, NhAssistantApiError, NhAssistantApiService, NhAssistantApprovalCardComponent, NhAssistantClientErrorCodes, NhAssistantComposerComponent, NhAssistantConnectionRequiredComponent, NhAssistantConversationListComponent, NhAssistantLauncherComponent, NhAssistantLiveService, NhAssistantMcpConnectService, NhAssistantMcpConnectionsComponent, NhAssistantMcpPopupAuthorizationWindow, NhAssistantPanelComponent, NhAssistantPanelService, NhAssistantPreferencesComponent, NhAssistantProposalFeedbackComponent, NhAssistantPushService, NhAssistantShareComponent, NhAssistantSseParser, NhAssistantStore, NhAssistantSubjectBadgeComponent, NhAssistantSubjectSummaryService, NhAssistantThreadComponent, NhAssistantToolCallCardComponent, NhAssistantToolCallGroupComponent, NhAssistantTranslationMerger, applyNhAssistantApprovalDecision, applyNhAssistantConversationChange, applyNhAssistantEvent, applyNhAssistantLiveEvent, describeNhAssistantClientContext, formatNhAssistantCountdown, nhAssistantCodeForStatus, nhAssistantConnectMcp, nhAssistantErrorMessageKey, nhAssistantIsUnread, nhAssistantLatestTurnHasText, nhAssistantMcpAuthorizationFromFragment, nhAssistantShareLink, nhAssistantXhrUpload, normalizeNhAssistantClientContext, provideNhAssistant, toNhAssistantError, NH_ASSISTANT_DASH_CASE as ɵNH_ASSISTANT_DASH_CASE, NhAssistantIconComponent as ɵNhAssistantIconComponent, NhAssistantMarkdownPipe as ɵNhAssistantMarkdownPipe, NhAssistantPickerComponent as ɵNhAssistantPickerComponent, NhAssistantSubjectLinkComponent as ɵNhAssistantSubjectLinkComponent, NhAssistantTranslatePipe as ɵNhAssistantTranslatePipe, nhAssistantCodeKeys as ɵnhAssistantCodeKeys, nhAssistantErrorKeys as ɵnhAssistantErrorKeys, nhAssistantErrorOf as ɵnhAssistantErrorOf, nhAssistantReloadOnAgentWork as ɵnhAssistantReloadOnAgentWork, nhAssistantWatchAgentWork as ɵnhAssistantWatchAgentWork, toNhAssistantError as ɵtoNhAssistantError };
12514
+ export { NH_ASSISTANT_ACCESS_POLICY, NH_ASSISTANT_CONFIG, NH_ASSISTANT_CONVERSATION_FRAGMENT, NH_ASSISTANT_ERROR_CODES, NH_ASSISTANT_FEEDBACK_CATEGORIES, NH_ASSISTANT_FEEDBACK_GROUPS, NH_ASSISTANT_FETCH, NH_ASSISTANT_ICONS, NH_ASSISTANT_JOIN_FRAGMENT, NH_ASSISTANT_MAX_BULK_FEEDBACK, NH_ASSISTANT_MAX_CUSTOM_INSTRUCTIONS, NH_ASSISTANT_MAX_FEEDBACK_TEXT, NH_ASSISTANT_MAX_PROPOSAL_LOOKUP, NH_ASSISTANT_MAX_SUBJECT_SUMMARY, NH_ASSISTANT_MCP_AUTHORIZATION_MESSAGE, NH_ASSISTANT_MCP_AUTHORIZATION_WINDOW, NH_ASSISTANT_MESSAGE_QUEUED_NOTICE_CODE, NH_ASSISTANT_NO_ANSWER_NOTICE_CODE, NH_ASSISTANT_PAGE_CONTEXT_LIMITS, NH_ASSISTANT_PANEL_MOBILE_QUERY, NH_ASSISTANT_PANEL_WIDTH, NH_ASSISTANT_RESULT_PREVIEW_LENGTH, NH_ASSISTANT_RUN_HISTORY_MAX_PAGE, NH_ASSISTANT_RUN_RESULT_MAX_LENGTH, NH_ASSISTANT_SERVER_ERROR_CODES, NH_ASSISTANT_SSE_EVENT_TYPES, NH_ASSISTANT_SUBJECT_SEARCH, NH_ASSISTANT_SYSTEM_GLOBAL_ID, NH_ASSISTANT_SYSTEM_SUBJECT_TYPE, NH_ASSISTANT_TRANSLATIONS, NH_ASSISTANT_UPLOAD, NH_ASSISTANT_UPLOAD_HEADER, NH_ASSISTANT_VIRTUAL_SCROLL_THRESHOLD, NhAssistantActivityComponent, NhAssistantAdminApiService, NhAssistantAgentPickerComponent, NhAssistantAgentRunApiService, NhAssistantAllowAllAccessPolicy, NhAssistantApiError, NhAssistantApiService, NhAssistantApprovalCardComponent, NhAssistantClientErrorCodes, NhAssistantComposerComponent, NhAssistantConnectionRequiredComponent, NhAssistantConversationListComponent, NhAssistantLauncherComponent, NhAssistantLiveService, NhAssistantMcpConnectService, NhAssistantMcpConnectionsComponent, NhAssistantMcpPopupAuthorizationWindow, NhAssistantPanelComponent, NhAssistantPanelService, NhAssistantPreferencesComponent, NhAssistantProposalFeedbackComponent, NhAssistantPushService, NhAssistantShareComponent, NhAssistantSseParser, NhAssistantStore, NhAssistantSubjectBadgeComponent, NhAssistantSubjectSummaryService, NhAssistantThreadComponent, NhAssistantToolCallCardComponent, NhAssistantToolCallGroupComponent, NhAssistantTranslationMerger, applyNhAssistantApprovalDecision, applyNhAssistantConversationChange, applyNhAssistantEvent, applyNhAssistantLiveEvent, describeNhAssistantClientContext, formatNhAssistantCountdown, nhAssistantCodeForStatus, nhAssistantConnectMcp, nhAssistantErrorMessageKey, nhAssistantIsUnread, nhAssistantLatestTurnHasText, nhAssistantMcpAuthorizationFromFragment, nhAssistantShareLink, nhAssistantXhrUpload, normalizeNhAssistantClientContext, provideNhAssistant, toNhAssistantError, NH_ASSISTANT_DASH_CASE as ɵNH_ASSISTANT_DASH_CASE, NhAssistantIconComponent as ɵNhAssistantIconComponent, NhAssistantMarkdownPipe as ɵNhAssistantMarkdownPipe, NhAssistantPickerComponent as ɵNhAssistantPickerComponent, NhAssistantSubjectLinkComponent as ɵNhAssistantSubjectLinkComponent, NhAssistantTranslatePipe as ɵNhAssistantTranslatePipe, nhAssistantCodeKeys as ɵnhAssistantCodeKeys, nhAssistantErrorKeys as ɵnhAssistantErrorKeys, nhAssistantErrorOf as ɵnhAssistantErrorOf, nhAssistantMediaTypeOf as ɵnhAssistantMediaTypeOf, nhAssistantReloadOnAgentWork as ɵnhAssistantReloadOnAgentWork, nhAssistantWatchAgentWork as ɵnhAssistantWatchAgentWork, toNhAssistantError as ɵtoNhAssistantError };
11628
12515
  //# sourceMappingURL=newheap-platform-ai-chat.mjs.map