@things-factory/ai-assistant 10.1.154 → 10.1.155

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.
Files changed (121) hide show
  1. package/client/bootstrap.ts +27 -0
  2. package/client/components/assistant-chat.ts +229 -178
  3. package/client/components/assistant-dock.ts +53 -48
  4. package/client/components/assistant-session-manager.ts +82 -100
  5. package/client/components/assistant-session-toolbar.ts +11 -3
  6. package/client/components/chat-input-builder.ts +8 -2
  7. package/client/settings/agent-settings-model.ts +164 -0
  8. package/client/settings/ai-agent-setting-let.ts +684 -0
  9. package/client/settings/ai-usage-setting-let.ts +252 -0
  10. package/client/settings/usage-model.ts +46 -0
  11. package/client/setup-assistant-dock.ts +2 -3
  12. package/client/utils/agent-choice.ts +56 -0
  13. package/client/utils/assistant-navigation.test.ts +14 -8
  14. package/client/utils/assistant-navigation.ts +8 -3
  15. package/client/utils/composer-frame.ts +28 -2
  16. package/client/utils/menu-room.ts +33 -0
  17. package/dist-client/bootstrap.d.ts +8 -0
  18. package/dist-client/bootstrap.js +26 -0
  19. package/dist-client/bootstrap.js.map +1 -0
  20. package/dist-client/components/assistant-chat.d.ts +32 -4
  21. package/dist-client/components/assistant-chat.js +234 -175
  22. package/dist-client/components/assistant-chat.js.map +1 -1
  23. package/dist-client/components/assistant-dock.d.ts +6 -8
  24. package/dist-client/components/assistant-dock.js +52 -60
  25. package/dist-client/components/assistant-dock.js.map +1 -1
  26. package/dist-client/components/assistant-session-manager.d.ts +8 -7
  27. package/dist-client/components/assistant-session-manager.js +84 -124
  28. package/dist-client/components/assistant-session-manager.js.map +1 -1
  29. package/dist-client/components/assistant-session-toolbar.d.ts +1 -0
  30. package/dist-client/components/assistant-session-toolbar.js +10 -3
  31. package/dist-client/components/assistant-session-toolbar.js.map +1 -1
  32. package/dist-client/components/chat-input-builder.d.ts +4 -0
  33. package/dist-client/components/chat-input-builder.js +4 -2
  34. package/dist-client/components/chat-input-builder.js.map +1 -1
  35. package/dist-client/settings/agent-settings-model.d.ts +93 -0
  36. package/dist-client/settings/agent-settings-model.js +95 -0
  37. package/dist-client/settings/agent-settings-model.js.map +1 -0
  38. package/dist-client/settings/ai-agent-setting-let.d.ts +51 -0
  39. package/dist-client/settings/ai-agent-setting-let.js +672 -0
  40. package/dist-client/settings/ai-agent-setting-let.js.map +1 -0
  41. package/dist-client/settings/ai-usage-setting-let.d.ts +26 -0
  42. package/dist-client/settings/ai-usage-setting-let.js +276 -0
  43. package/dist-client/settings/ai-usage-setting-let.js.map +1 -0
  44. package/dist-client/settings/usage-model.d.ts +32 -0
  45. package/dist-client/settings/usage-model.js +25 -0
  46. package/dist-client/settings/usage-model.js.map +1 -0
  47. package/dist-client/setup-assistant-dock.d.ts +0 -1
  48. package/dist-client/setup-assistant-dock.js +0 -1
  49. package/dist-client/setup-assistant-dock.js.map +1 -1
  50. package/dist-client/tsconfig.tsbuildinfo +1 -1
  51. package/dist-client/utils/agent-choice.d.ts +24 -0
  52. package/dist-client/utils/agent-choice.js +38 -0
  53. package/dist-client/utils/agent-choice.js.map +1 -0
  54. package/dist-client/utils/assistant-navigation.d.ts +8 -3
  55. package/dist-client/utils/assistant-navigation.js +7 -2
  56. package/dist-client/utils/assistant-navigation.js.map +1 -1
  57. package/dist-client/utils/assistant-navigation.test.js +14 -8
  58. package/dist-client/utils/assistant-navigation.test.js.map +1 -1
  59. package/dist-client/utils/composer-frame.d.ts +23 -2
  60. package/dist-client/utils/composer-frame.js +20 -2
  61. package/dist-client/utils/composer-frame.js.map +1 -1
  62. package/dist-client/utils/menu-room.d.ts +13 -0
  63. package/dist-client/utils/menu-room.js +26 -0
  64. package/dist-client/utils/menu-room.js.map +1 -0
  65. package/dist-server/service/ai-agent-resolver.d.ts +82 -0
  66. package/dist-server/service/ai-agent-resolver.js +417 -0
  67. package/dist-server/service/ai-agent-resolver.js.map +1 -0
  68. package/dist-server/service/assistant-capability-resolver.d.ts +1 -1
  69. package/dist-server/service/assistant-capability-resolver.js +7 -7
  70. package/dist-server/service/assistant-capability-resolver.js.map +1 -1
  71. package/dist-server/service/assistant-chat-resolver.d.ts +2 -0
  72. package/dist-server/service/assistant-chat-resolver.js +32 -15
  73. package/dist-server/service/assistant-chat-resolver.js.map +1 -1
  74. package/dist-server/service/assistant-mode/resolve.d.ts +0 -1
  75. package/dist-server/service/assistant-mode/resolve.js +0 -1
  76. package/dist-server/service/assistant-mode/resolve.js.map +1 -1
  77. package/dist-server/service/assistant-mode/types.d.ts +1 -2
  78. package/dist-server/service/assistant-mode/types.js.map +1 -1
  79. package/dist-server/service/chat-session/chat-session.d.ts +2 -0
  80. package/dist-server/service/chat-session/chat-session.js +5 -0
  81. package/dist-server/service/chat-session/chat-session.js.map +1 -1
  82. package/dist-server/service/index.d.ts +2 -1
  83. package/dist-server/service/index.js +3 -0
  84. package/dist-server/service/index.js.map +1 -1
  85. package/dist-server/service/testing/mode-conformance.js +0 -1
  86. package/dist-server/service/testing/mode-conformance.js.map +1 -1
  87. package/dist-server/tsconfig.tsbuildinfo +1 -1
  88. package/package.json +9 -7
  89. package/server/service/ai-agent-resolver.ts +330 -0
  90. package/server/service/assistant-capability-resolver.ts +7 -7
  91. package/server/service/assistant-chat-resolver.ts +32 -24
  92. package/server/service/assistant-mode/resolve.test.ts +0 -2
  93. package/server/service/assistant-mode/resolve.ts +0 -2
  94. package/server/service/assistant-mode/types.ts +1 -2
  95. package/server/service/chat-session/chat-session.ts +5 -0
  96. package/server/service/index.ts +3 -0
  97. package/server/service/testing/mode-conformance.ts +0 -1
  98. package/server/tool-registry-boot-report.test.ts +2 -0
  99. package/test/ai-agent-settings.test.ts +123 -0
  100. package/test/assistant-dock.test.ts +3 -2
  101. package/test/assistant-session-manager.test.ts +10 -4
  102. package/test/assistant-session-toolbar.test.ts +8 -0
  103. package/test/composer-frame.test.ts +40 -2
  104. package/test/menu-room.test.ts +33 -0
  105. package/test/open-doors.test.ts +3 -1
  106. package/test/session-discovery.test.ts +7 -6
  107. package/things-factory.config.js +2 -0
  108. package/translations/en.json +78 -12
  109. package/translations/es.json +78 -12
  110. package/translations/ja.json +78 -12
  111. package/translations/ko.json +78 -12
  112. package/translations/ms.json +78 -12
  113. package/translations/zh.json +78 -12
  114. package/client/components/chat-triggers.test.ts +0 -72
  115. package/client/components/chat-triggers.ts +0 -62
  116. package/dist-client/components/chat-triggers.d.ts +0 -16
  117. package/dist-client/components/chat-triggers.js +0 -42
  118. package/dist-client/components/chat-triggers.js.map +0 -1
  119. package/dist-client/components/chat-triggers.test.d.ts +0 -1
  120. package/dist-client/components/chat-triggers.test.js +0 -61
  121. package/dist-client/components/chat-triggers.test.js.map +0 -1
@@ -1,42 +0,0 @@
1
- /*
2
- * Which trigger characters a chat surface can actually use — **asked, not declared.**
3
- *
4
- * All three chips (`#` `@` `/`) were always drawn. On operato-figure that taught two things the
5
- * surface cannot do: `#` mentions components of a document figure has none of, and `/` inserts
6
- * templates figure registered none of, so the popup opens empty.
7
- *
8
- * A chip that opens nothing is worse than an absent one. The reader does not conclude "not
9
- * applicable here" — they conclude the feature is broken, and they are half right.
10
- *
11
- * So each chip is read off the thing it needs. A host that gains a document, a stored session or
12
- * a template set gets the chip with no extra declaration; one that never does never shows it.
13
- * Same shape as lite-menu deciding whether the board menu type is selectable by asking whether
14
- * its renderer is registered, rather than declaring that it is.
15
- */
16
- /** Order is fixed: `#` `@` `/`. It is the order people learned, so it does not follow availability. */
17
- export function availableTriggers(availability) {
18
- const chips = [];
19
- if (availability.hasMentionableThings) {
20
- chips.push({
21
- char: '#',
22
- labelKey: 'ai-assistant.label.trigger-component',
23
- tooltipKey: 'ai-assistant.text.trigger-component-tooltip'
24
- });
25
- }
26
- if (availability.hasSession) {
27
- chips.push({
28
- char: '@',
29
- labelKey: 'ai-assistant.label.trigger-user',
30
- tooltipKey: 'ai-assistant.text.trigger-user-tooltip'
31
- });
32
- }
33
- if (availability.slashTemplateCount > 0) {
34
- chips.push({
35
- char: '/',
36
- labelKey: 'ai-assistant.label.trigger-insert',
37
- tooltipKey: 'ai-assistant.text.trigger-insert-tooltip'
38
- });
39
- }
40
- return chips;
41
- }
42
- //# sourceMappingURL=chat-triggers.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"chat-triggers.js","sourceRoot":"","sources":["../../client/components/chat-triggers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAkBH,uGAAuG;AACvG,MAAM,UAAU,iBAAiB,CAAC,YAAiC;IACjE,MAAM,KAAK,GAAkB,EAAE,CAAA;IAE/B,IAAI,YAAY,CAAC,oBAAoB,EAAE,CAAC;QACtC,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,sCAAsC;YAChD,UAAU,EAAE,6CAA6C;SAC1D,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,YAAY,CAAC,UAAU,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,iCAAiC;YAC3C,UAAU,EAAE,wCAAwC;SACrD,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,YAAY,CAAC,kBAAkB,GAAG,CAAC,EAAE,CAAC;QACxC,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,mCAAmC;YAC7C,UAAU,EAAE,0CAA0C;SACvD,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC","sourcesContent":["/*\n * Which trigger characters a chat surface can actually use — **asked, not declared.**\n *\n * All three chips (`#` `@` `/`) were always drawn. On operato-figure that taught two things the\n * surface cannot do: `#` mentions components of a document figure has none of, and `/` inserts\n * templates figure registered none of, so the popup opens empty.\n *\n * A chip that opens nothing is worse than an absent one. The reader does not conclude \"not\n * applicable here\" — they conclude the feature is broken, and they are half right.\n *\n * So each chip is read off the thing it needs. A host that gains a document, a stored session or\n * a template set gets the chip with no extra declaration; one that never does never shows it.\n * Same shape as lite-menu deciding whether the board menu type is selectable by asking whether\n * its renderer is registered, rather than declaring that it is.\n */\n\n/** What the surface has to offer, as plain facts. The caller reads these off itself. */\nexport interface TriggerAvailability {\n /** Something to mention: a live document, a static one, or a declared type list. */\n hasMentionableThings: boolean\n /** A stored conversation. A `@` mention notifies a participant, and an ad-hoc turn has none. */\n hasSession: boolean\n /** How many insertable templates the host registered. */\n slashTemplateCount: number\n}\n\nexport interface TriggerChip {\n char: '#' | '@' | '/'\n labelKey: string\n tooltipKey: string\n}\n\n/** Order is fixed: `#` `@` `/`. It is the order people learned, so it does not follow availability. */\nexport function availableTriggers(availability: TriggerAvailability): TriggerChip[] {\n const chips: TriggerChip[] = []\n\n if (availability.hasMentionableThings) {\n chips.push({\n char: '#',\n labelKey: 'ai-assistant.label.trigger-component',\n tooltipKey: 'ai-assistant.text.trigger-component-tooltip'\n })\n }\n\n if (availability.hasSession) {\n chips.push({\n char: '@',\n labelKey: 'ai-assistant.label.trigger-user',\n tooltipKey: 'ai-assistant.text.trigger-user-tooltip'\n })\n }\n\n if (availability.slashTemplateCount > 0) {\n chips.push({\n char: '/',\n labelKey: 'ai-assistant.label.trigger-insert',\n tooltipKey: 'ai-assistant.text.trigger-insert-tooltip'\n })\n }\n\n return chips\n}\n"]}
@@ -1 +0,0 @@
1
- export {};
@@ -1,61 +0,0 @@
1
- /*
2
- * What this guards: **the empty chat does not teach what this surface cannot do.**
3
- *
4
- * operato-figure mounted the chat and its empty screen offered all three triggers. `#` mentions
5
- * components of a document figure has none of; `/` inserts templates figure registered none of,
6
- * so the popup opens empty. Neither errors — they just do nothing, which reads as broken rather
7
- * than not applicable.
8
- */
9
- import { availableTriggers } from './chat-triggers';
10
- const chars = (availability) => availableTriggers(availability).map(chip => chip.char);
11
- const board = { hasMentionableThings: true, hasSession: true, slashTemplateCount: 12 };
12
- const figure = { hasMentionableThings: false, hasSession: true, slashTemplateCount: 0 };
13
- describe('a board authoring surface', () => {
14
- it('offers all three', () => {
15
- expect(chars(board)).toEqual(['#', '@', '/']);
16
- });
17
- });
18
- describe('a surface with no document of its own', () => {
19
- it('★ does not offer # — there is nothing to mention', () => {
20
- expect(chars(figure)).not.toContain('#');
21
- });
22
- it('★ does not offer / — nobody registered a template, so the popup would open empty', () => {
23
- expect(chars(figure)).not.toContain('/');
24
- });
25
- it('still offers @ — mentioning a colleague works wherever a conversation is stored', () => {
26
- expect(chars(figure)).toEqual(['@']);
27
- });
28
- });
29
- describe('each chip is read off the one thing it needs', () => {
30
- it('a declared type list is enough for # — a live document is not required', () => {
31
- expect(chars({ ...figure, hasMentionableThings: true })).toContain('#');
32
- });
33
- it('an ad-hoc turn has no @ — a mention notifies a participant and there is no conversation to join', () => {
34
- expect(chars({ ...board, hasSession: false })).not.toContain('@');
35
- });
36
- it('one registered template is enough for /', () => {
37
- expect(chars({ ...figure, slashTemplateCount: 1 })).toContain('/');
38
- });
39
- });
40
- describe('what the row looks like when nothing is available', () => {
41
- it('★ empty, so the caller can leave the whole row out rather than draw a heading over nothing', () => {
42
- expect(availableTriggers({ hasMentionableThings: false, hasSession: false, slashTemplateCount: 0 })).toEqual([]);
43
- });
44
- });
45
- describe('order', () => {
46
- it('stays # @ / regardless of which ones survive — it is the order people learned', () => {
47
- expect(chars({ hasMentionableThings: true, hasSession: false, slashTemplateCount: 3 })).toEqual(['#', '/']);
48
- });
49
- });
50
- describe('every chip carries its own words', () => {
51
- it('label and tooltip keys are distinct per chip — one shared key would caption them all alike', () => {
52
- const chips = availableTriggers(board);
53
- const labels = new Set(chips.map(chip => chip.labelKey));
54
- const tooltips = new Set(chips.map(chip => chip.tooltipKey));
55
- expect(labels.size).toBe(chips.length);
56
- expect(tooltips.size).toBe(chips.length);
57
- for (const chip of chips)
58
- expect(chip.labelKey.startsWith('ai-assistant.')).toBe(true);
59
- });
60
- });
61
- //# sourceMappingURL=chat-triggers.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"chat-triggers.test.js","sourceRoot":"","sources":["../../client/components/chat-triggers.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * What this guards: **the empty chat does not teach what this surface cannot do.**\n *\n * operato-figure mounted the chat and its empty screen offered all three triggers. `#` mentions\n * components of a document figure has none of; `/` inserts templates figure registered none of,\n * so the popup opens empty. Neither errors — they just do nothing, which reads as broken rather\n * than not applicable.\n */\nimport { availableTriggers } from './chat-triggers'\n\nconst chars = (availability: any) => availableTriggers(availability).map(chip => chip.char)\n\nconst board = { hasMentionableThings: true, hasSession: true, slashTemplateCount: 12 }\nconst figure = { hasMentionableThings: false, hasSession: true, slashTemplateCount: 0 }\n\ndescribe('a board authoring surface', () => {\n it('offers all three', () => {\n expect(chars(board)).toEqual(['#', '@', '/'])\n })\n})\n\ndescribe('a surface with no document of its own', () => {\n it('★ does not offer # — there is nothing to mention', () => {\n expect(chars(figure)).not.toContain('#')\n })\n\n it('★ does not offer / — nobody registered a template, so the popup would open empty', () => {\n expect(chars(figure)).not.toContain('/')\n })\n\n it('still offers @ — mentioning a colleague works wherever a conversation is stored', () => {\n expect(chars(figure)).toEqual(['@'])\n })\n})\n\ndescribe('each chip is read off the one thing it needs', () => {\n it('a declared type list is enough for # — a live document is not required', () => {\n expect(chars({ ...figure, hasMentionableThings: true })).toContain('#')\n })\n\n it('an ad-hoc turn has no @ — a mention notifies a participant and there is no conversation to join', () => {\n expect(chars({ ...board, hasSession: false })).not.toContain('@')\n })\n\n it('one registered template is enough for /', () => {\n expect(chars({ ...figure, slashTemplateCount: 1 })).toContain('/')\n })\n})\n\ndescribe('what the row looks like when nothing is available', () => {\n it('★ empty, so the caller can leave the whole row out rather than draw a heading over nothing', () => {\n expect(availableTriggers({ hasMentionableThings: false, hasSession: false, slashTemplateCount: 0 })).toEqual([])\n })\n})\n\ndescribe('order', () => {\n it('stays # @ / regardless of which ones survive — it is the order people learned', () => {\n expect(chars({ hasMentionableThings: true, hasSession: false, slashTemplateCount: 3 })).toEqual(['#', '/'])\n })\n})\n\ndescribe('every chip carries its own words', () => {\n it('label and tooltip keys are distinct per chip — one shared key would caption them all alike', () => {\n const chips = availableTriggers(board)\n const labels = new Set(chips.map(chip => chip.labelKey))\n const tooltips = new Set(chips.map(chip => chip.tooltipKey))\n\n expect(labels.size).toBe(chips.length)\n expect(tooltips.size).toBe(chips.length)\n for (const chip of chips) expect(chip.labelKey.startsWith('ai-assistant.')).toBe(true)\n })\n})\n"]}