@jskit-ai/assistant-runtime 0.1.238 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +138 -0
- package/locales/en.json +136 -0
- package/package.json +10 -9
- package/src/client/components/AssistantConversationClientElement.vue +11 -6
- package/src/client/components/AssistantSettingsClientElement.vue +19 -14
- package/src/client/components/AssistantSurfaceClientElement.vue +52 -29
- package/src/client/composables/useAssistantConversation.js +48 -29
- package/src/client/composables/useAssistantRuntime.js +18 -18
- package/src/client/i18n.js +10 -0
- package/src/client/support/assistantRuntimeState.js +15 -2
- package/src/client/support/subscribeAssistantConversation.js +3 -1
- package/src/server/actions.js +1 -1
- package/src/server/createAssistantRuntime.js +4 -2
- package/src/server/registerConversationSubscriptions.js +8 -4
- package/src/server/registerFastifyConversations.js +1 -1
- package/src/server/registerRoutes.js +8 -3
- package/src/server/services/assistantConfigService.js +1 -1
- package/src/server/services/chatService.js +38 -25
- package/src/server/services/transcriptService.js +8 -12
- package/src/server/sqlConversationStorage.js +1 -1
- package/src/server/support/assistantErrors.js +16 -1
- package/src/server/support/assistantRouteContext.js +5 -3
- package/src/server/support/assistantSurface.js +1 -1
- package/test/assistantRuntimeState.test.js +16 -0
- package/test/i18nCatalog.test.js +16 -0
- package/test/lazyAppConfig.test.js +24 -2
- package/test/turnRequests.test.js +12 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0 - 2026-10-29
|
|
4
|
+
|
|
5
|
+
- Translation ready: every user-visible text is a message key with its English in `locales/en.json`, so an app translates it by supplying translation files only. With no translations, texts and behaviour are unchanged. See the guide "Translating a JSKIT app".
|
|
6
|
+
|
|
7
|
+
### New message keys (90)
|
|
8
|
+
|
|
9
|
+
All keys in `locales/en.json` (exported as `./locales/en.json`) are new in this version.
|
|
10
|
+
|
|
11
|
+
<details><summary>Key list</summary>
|
|
12
|
+
|
|
13
|
+
- `jskit.assistant.runtime.authenticationRequired`
|
|
14
|
+
- `jskit.assistant.runtime.workspace.selectionRequired`
|
|
15
|
+
- `jskit.assistant.runtime.route.surfaceHeaderRequired`
|
|
16
|
+
- `jskit.assistant.runtime.route.notFound`
|
|
17
|
+
- `jskit.assistant.runtime.route.surfaceForbidden`
|
|
18
|
+
- `jskit.assistant.runtime.route.assistantNotFound`
|
|
19
|
+
- `jskit.assistant.runtime.connection.unavailable`
|
|
20
|
+
- `jskit.assistant.runtime.connection.selectionNotAllowed`
|
|
21
|
+
- `jskit.assistant.runtime.connection.applicationAi`
|
|
22
|
+
- `jskit.assistant.runtime.connection.applicationDefault`
|
|
23
|
+
- `jskit.assistant.runtime.provider.notConfigured`
|
|
24
|
+
- `jskit.assistant.runtime.conversation.notFound`
|
|
25
|
+
- `jskit.assistant.runtime.conversation.idInvalid`
|
|
26
|
+
- `jskit.assistant.runtime.conversation.createFailed`
|
|
27
|
+
- `jskit.assistant.runtime.conversation.assistantLabel`
|
|
28
|
+
- `jskit.assistant.runtime.conversation.welcomeMessage`
|
|
29
|
+
- `jskit.assistant.runtime.conversation.placeholder`
|
|
30
|
+
- `jskit.assistant.runtime.conversation.startedAtUnknown`
|
|
31
|
+
- `jskit.assistant.runtime.conversation.status.active`
|
|
32
|
+
- `jskit.assistant.runtime.conversation.status.completed`
|
|
33
|
+
- `jskit.assistant.runtime.conversation.status.failed`
|
|
34
|
+
- `jskit.assistant.runtime.conversation.status.aborted`
|
|
35
|
+
- `jskit.assistant.runtime.conversation.status.unknown`
|
|
36
|
+
- `jskit.assistant.runtime.request.messageIdInvalid`
|
|
37
|
+
- `jskit.assistant.runtime.request.inputRequired`
|
|
38
|
+
- `jskit.assistant.runtime.request.messageIdConflict`
|
|
39
|
+
- `jskit.assistant.runtime.request.unconfirmed`
|
|
40
|
+
- `jskit.assistant.runtime.request.failed`
|
|
41
|
+
- `jskit.assistant.runtime.request.cancelled`
|
|
42
|
+
- `jskit.assistant.runtime.request.noOutput`
|
|
43
|
+
- `jskit.assistant.runtime.request.notConfirmed`
|
|
44
|
+
- `jskit.assistant.runtime.attachments.invalidIds`
|
|
45
|
+
- `jskit.assistant.runtime.attachments.notConfigured`
|
|
46
|
+
- `jskit.assistant.runtime.attachments.noLongerAccepted`
|
|
47
|
+
- `jskit.assistant.runtime.attachments.clearFailed`
|
|
48
|
+
- `jskit.assistant.runtime.subscription.idRequired`
|
|
49
|
+
- `jskit.assistant.runtime.subscription.failed`
|
|
50
|
+
- `jskit.assistant.runtime.history.refreshFailed`
|
|
51
|
+
- `jskit.assistant.runtime.history.loadFailed`
|
|
52
|
+
- `jskit.assistant.runtime.history.listFailed`
|
|
53
|
+
- `jskit.assistant.runtime.history.conversationLoadFailed`
|
|
54
|
+
- `jskit.assistant.runtime.accessDenied`
|
|
55
|
+
- `jskit.assistant.runtime.delivery.waitingForStep`
|
|
56
|
+
- `jskit.assistant.runtime.delivery.unconfirmedBeforeClose`
|
|
57
|
+
- `jskit.assistant.runtime.delivery.stillUnknown`
|
|
58
|
+
- `jskit.assistant.runtime.delivery.checkFailed`
|
|
59
|
+
- `jskit.assistant.runtime.delivery.notConfirmed`
|
|
60
|
+
- `jskit.assistant.runtime.goal.budgetsUnsupported`
|
|
61
|
+
- `jskit.assistant.runtime.goal.changeFailed`
|
|
62
|
+
- `jskit.assistant.runtime.goal.unavailable`
|
|
63
|
+
- `jskit.assistant.runtime.goal.resumeMessage`
|
|
64
|
+
- `jskit.assistant.runtime.goal.cancelMessage`
|
|
65
|
+
- `jskit.assistant.runtime.composer.questionsPlaceholder`
|
|
66
|
+
- `jskit.assistant.runtime.composer.choicesPlaceholder`
|
|
67
|
+
- `jskit.assistant.runtime.composer.questionsHint`
|
|
68
|
+
- `jskit.assistant.runtime.tool.progress`
|
|
69
|
+
- `jskit.assistant.runtime.tool.status.pending`
|
|
70
|
+
- `jskit.assistant.runtime.tool.status.done`
|
|
71
|
+
- `jskit.assistant.runtime.tool.status.failed`
|
|
72
|
+
- `jskit.assistant.runtime.tool.status.interrupted`
|
|
73
|
+
- `jskit.assistant.runtime.surface.newConversation`
|
|
74
|
+
- `jskit.assistant.runtime.surface.conversations`
|
|
75
|
+
- `jskit.assistant.runtime.surface.activity`
|
|
76
|
+
- `jskit.assistant.runtime.surface.toolActivity`
|
|
77
|
+
- `jskit.assistant.runtime.surface.conversation`
|
|
78
|
+
- `jskit.assistant.runtime.surface.responseCanceled`
|
|
79
|
+
- `jskit.assistant.runtime.surface.startNewConversation`
|
|
80
|
+
- `jskit.assistant.runtime.surface.refresh`
|
|
81
|
+
- `jskit.assistant.runtime.surface.close`
|
|
82
|
+
- `jskit.assistant.runtime.surface.loadingConversationsLabel`
|
|
83
|
+
- `jskit.assistant.runtime.surface.loadingConversations`
|
|
84
|
+
- `jskit.assistant.runtime.surface.savedConversations`
|
|
85
|
+
- `jskit.assistant.runtime.surface.noSavedConversations`
|
|
86
|
+
- `jskit.assistant.runtime.surface.untitledConversation`
|
|
87
|
+
- `jskit.assistant.runtime.surface.loadOlderConversations`
|
|
88
|
+
- `jskit.assistant.runtime.surface.messageCount`
|
|
89
|
+
- `jskit.assistant.runtime.viewer.you`
|
|
90
|
+
- `jskit.assistant.runtime.settings.title`
|
|
91
|
+
- `jskit.assistant.runtime.settings.noPermission`
|
|
92
|
+
- `jskit.assistant.runtime.settings.save`
|
|
93
|
+
- `jskit.assistant.runtime.settings.systemPrompt`
|
|
94
|
+
- `jskit.assistant.runtime.settings.subtitle`
|
|
95
|
+
- `jskit.assistant.runtime.settings.subtitleWorkspace`
|
|
96
|
+
- `jskit.assistant.runtime.settings.subtitleSurface`
|
|
97
|
+
- `jskit.assistant.runtime.settings.notConfigured`
|
|
98
|
+
- `jskit.assistant.runtime.settings.workspaceUnavailable`
|
|
99
|
+
- `jskit.assistant.runtime.settings.selectWorkspace`
|
|
100
|
+
- `jskit.assistant.runtime.settings.updateFailed`
|
|
101
|
+
- `jskit.errors.assistant_history_inspection_required`
|
|
102
|
+
- `jskit.errors.assistant_subscription_transport_failed`
|
|
103
|
+
|
|
104
|
+
</details>
|
|
105
|
+
|
|
106
|
+
### Codes (27)
|
|
107
|
+
|
|
108
|
+
Error, field-error and message codes this package sends, with their English. Undotted codes kept their existing values and render from `jskit.errors.<code>`.
|
|
109
|
+
|
|
110
|
+
| Code | English |
|
|
111
|
+
| --- | --- |
|
|
112
|
+
| `assistant.runtime.authenticationRequired` | Authentication required. |
|
|
113
|
+
| `assistant.runtime.workspace.selectionRequired` | Workspace selection required. |
|
|
114
|
+
| `assistant.runtime.route.surfaceHeaderRequired` | Assistant surface header x-jskit-surface is required. |
|
|
115
|
+
| `assistant.runtime.route.notFound` | Assistant route not found. |
|
|
116
|
+
| `assistant.runtime.route.surfaceForbidden` | Assistant route is not available on this surface. |
|
|
117
|
+
| `assistant.runtime.route.assistantNotFound` | Assistant not found. |
|
|
118
|
+
| `assistant.runtime.connection.unavailable` | This AI connection is not available in this assistant. |
|
|
119
|
+
| `assistant.runtime.connection.selectionNotAllowed` | This assistant does not allow selecting an AI connection. |
|
|
120
|
+
| `assistant.runtime.provider.notConfigured` | Assistant provider is not configured. |
|
|
121
|
+
| `assistant.runtime.conversation.notFound` | Conversation not found. |
|
|
122
|
+
| `assistant.runtime.conversation.idInvalid` | conversationId must be a valid record id. |
|
|
123
|
+
| `assistant.runtime.conversation.createFailed` | Assistant failed to create conversation. |
|
|
124
|
+
| `assistant.runtime.request.messageIdInvalid` | messageId is required and must be at most 128 characters. |
|
|
125
|
+
| `assistant.runtime.request.inputRequired` | input is required. |
|
|
126
|
+
| `assistant.runtime.request.messageIdConflict` | This message ID belongs to a different request. |
|
|
127
|
+
| `assistant.runtime.request.unconfirmed` | This message was already submitted. Its completion is not confirmed; check conversation history before sending new work. |
|
|
128
|
+
| `assistant.runtime.request.failed` | Assistant request failed. |
|
|
129
|
+
| `assistant.runtime.request.cancelled` | Assistant request was cancelled. |
|
|
130
|
+
| `assistant.runtime.request.noOutput` | Assistant returned no output. |
|
|
131
|
+
| `assistant.runtime.attachments.invalidIds` | Invalid attachment identifiers. |
|
|
132
|
+
| `assistant.runtime.attachments.notConfigured` | File attachments are not configured for this assistant. |
|
|
133
|
+
| `assistant.runtime.subscription.idRequired` | A conversation subscription id is required. |
|
|
134
|
+
| `assistant.runtime.subscription.failed` | Conversation subscription failed. |
|
|
135
|
+
| `assistant.runtime.history.refreshFailed` | Older conversation history could not be refreshed. Reload chat to try again. |
|
|
136
|
+
| `assistant.runtime.accessDenied` | Access denied. |
|
|
137
|
+
| `assistant_history_inspection_required` | This conversation has ambiguous historical SQL rows. Inspect the reported rows offline before using the common runtime; no history was changed. |
|
|
138
|
+
| `assistant_subscription_transport_failed` | Chat updates could not reconnect. Retrying automatically; Reload chat to try now. |
|
package/locales/en.json
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
{
|
|
2
|
+
"jskit": {
|
|
3
|
+
"assistant": {
|
|
4
|
+
"runtime": {
|
|
5
|
+
"authenticationRequired": "Authentication required.",
|
|
6
|
+
"workspace": {
|
|
7
|
+
"selectionRequired": "Workspace selection required."
|
|
8
|
+
},
|
|
9
|
+
"route": {
|
|
10
|
+
"surfaceHeaderRequired": "Assistant surface header x-jskit-surface is required.",
|
|
11
|
+
"notFound": "Assistant route not found.",
|
|
12
|
+
"surfaceForbidden": "Assistant route is not available on this surface.",
|
|
13
|
+
"assistantNotFound": "Assistant not found."
|
|
14
|
+
},
|
|
15
|
+
"connection": {
|
|
16
|
+
"unavailable": "This AI connection is not available in this assistant.",
|
|
17
|
+
"selectionNotAllowed": "This assistant does not allow selecting an AI connection.",
|
|
18
|
+
"applicationAi": "Application AI",
|
|
19
|
+
"applicationDefault": "Application default"
|
|
20
|
+
},
|
|
21
|
+
"provider": {
|
|
22
|
+
"notConfigured": "Assistant provider is not configured."
|
|
23
|
+
},
|
|
24
|
+
"conversation": {
|
|
25
|
+
"notFound": "Conversation not found.",
|
|
26
|
+
"idInvalid": "conversationId must be a valid record id.",
|
|
27
|
+
"createFailed": "Assistant failed to create conversation.",
|
|
28
|
+
"assistantLabel": "Assistant",
|
|
29
|
+
"welcomeMessage": "What would you like to do?",
|
|
30
|
+
"placeholder": "Message the assistant…",
|
|
31
|
+
"startedAtUnknown": "unknown",
|
|
32
|
+
"status": {
|
|
33
|
+
"active": "active",
|
|
34
|
+
"completed": "completed",
|
|
35
|
+
"failed": "failed",
|
|
36
|
+
"aborted": "aborted",
|
|
37
|
+
"unknown": "unknown"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"request": {
|
|
41
|
+
"messageIdInvalid": "messageId is required and must be at most 128 characters.",
|
|
42
|
+
"inputRequired": "input is required.",
|
|
43
|
+
"messageIdConflict": "This message ID belongs to a different request.",
|
|
44
|
+
"unconfirmed": "This message was already submitted. Its completion is not confirmed; check conversation history before sending new work.",
|
|
45
|
+
"failed": "Assistant request failed.",
|
|
46
|
+
"cancelled": "Assistant request was cancelled.",
|
|
47
|
+
"noOutput": "Assistant returned no output.",
|
|
48
|
+
"notConfirmed": "Assistant did not confirm this message."
|
|
49
|
+
},
|
|
50
|
+
"attachments": {
|
|
51
|
+
"invalidIds": "Invalid attachment identifiers.",
|
|
52
|
+
"notConfigured": "File attachments are not configured for this assistant.",
|
|
53
|
+
"noLongerAccepted": "This conversation no longer accepts attachments.",
|
|
54
|
+
"clearFailed": "Accepted attachments could not be cleared from the composer."
|
|
55
|
+
},
|
|
56
|
+
"subscription": {
|
|
57
|
+
"idRequired": "A conversation subscription id is required.",
|
|
58
|
+
"failed": "Conversation subscription failed."
|
|
59
|
+
},
|
|
60
|
+
"history": {
|
|
61
|
+
"refreshFailed": "Older conversation history could not be refreshed. Reload chat to try again.",
|
|
62
|
+
"loadFailed": "Older conversation history could not be loaded.",
|
|
63
|
+
"listFailed": "Unable to load conversation history.",
|
|
64
|
+
"conversationLoadFailed": "Unable to load conversation."
|
|
65
|
+
},
|
|
66
|
+
"accessDenied": "Access denied.",
|
|
67
|
+
"delivery": {
|
|
68
|
+
"waitingForStep": "Waiting for the assistant to finish this step",
|
|
69
|
+
"unconfirmedBeforeClose": "Delivery was not confirmed before this page closed. Check the conversation before retrying.",
|
|
70
|
+
"stillUnknown": "Delivery is still unknown. Retry to check again.",
|
|
71
|
+
"checkFailed": "Delivery could not be checked.",
|
|
72
|
+
"notConfirmed": "Message delivery is not confirmed."
|
|
73
|
+
},
|
|
74
|
+
"goal": {
|
|
75
|
+
"budgetsUnsupported": "This conversation does not support goal token budgets.",
|
|
76
|
+
"changeFailed": "Could not change the goal.",
|
|
77
|
+
"unavailable": "Goal status is unavailable.",
|
|
78
|
+
"resumeMessage": "Resume the goal.",
|
|
79
|
+
"cancelMessage": "Cancel the goal."
|
|
80
|
+
},
|
|
81
|
+
"composer": {
|
|
82
|
+
"questionsPlaceholder": "Optional additional context…",
|
|
83
|
+
"choicesPlaceholder": "Choose an answer above, or type another answer…",
|
|
84
|
+
"questionsHint": "Answer the assistant, then send one combined reply."
|
|
85
|
+
},
|
|
86
|
+
"tool": {
|
|
87
|
+
"progress": "{name} — {status}",
|
|
88
|
+
"status": {
|
|
89
|
+
"pending": "pending",
|
|
90
|
+
"done": "done",
|
|
91
|
+
"failed": "failed",
|
|
92
|
+
"interrupted": "interrupted"
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"surface": {
|
|
96
|
+
"newConversation": "New conversation",
|
|
97
|
+
"conversations": "Conversations",
|
|
98
|
+
"activity": "Activity",
|
|
99
|
+
"toolActivity": "Tool activity",
|
|
100
|
+
"conversation": "Conversation",
|
|
101
|
+
"responseCanceled": "Response canceled. A tool already running may still finish.",
|
|
102
|
+
"startNewConversation": "Start new conversation",
|
|
103
|
+
"refresh": "Refresh",
|
|
104
|
+
"close": "Close",
|
|
105
|
+
"loadingConversationsLabel": "Loading conversations",
|
|
106
|
+
"loadingConversations": "Loading conversations…",
|
|
107
|
+
"savedConversations": "Saved conversations",
|
|
108
|
+
"noSavedConversations": "No saved conversations",
|
|
109
|
+
"untitledConversation": "Untitled conversation",
|
|
110
|
+
"loadOlderConversations": "Load older conversations",
|
|
111
|
+
"messageCount": "{count} messages"
|
|
112
|
+
},
|
|
113
|
+
"viewer": {
|
|
114
|
+
"you": "You"
|
|
115
|
+
},
|
|
116
|
+
"settings": {
|
|
117
|
+
"title": "Assistant settings",
|
|
118
|
+
"noPermission": "You do not have permission to view assistant settings.",
|
|
119
|
+
"save": "Save assistant settings",
|
|
120
|
+
"systemPrompt": "System prompt",
|
|
121
|
+
"subtitle": "Configure assistant settings.",
|
|
122
|
+
"subtitleWorkspace": "Configure the prompt used on the {surface} surface for this workspace.",
|
|
123
|
+
"subtitleSurface": "Configure the prompt used on the {surface} surface.",
|
|
124
|
+
"notConfigured": "Assistant settings are not configured for this surface.",
|
|
125
|
+
"workspaceUnavailable": "Workspace support is not available for this assistant surface.",
|
|
126
|
+
"selectWorkspace": "Select a workspace to configure assistant settings.",
|
|
127
|
+
"updateFailed": "Unable to update assistant settings."
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"errors": {
|
|
132
|
+
"assistant_history_inspection_required": "This conversation has ambiguous historical SQL rows. Inspect the reported rows offline before using the common runtime; no history was changed.",
|
|
133
|
+
"assistant_subscription_transport_failed": "Chat updates could not reconnect. Retrying automatically; Reload chat to try now."
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/assistant-runtime",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test --test-concurrency=1"
|
|
@@ -9,22 +9,23 @@
|
|
|
9
9
|
"./client": "./src/client/index.js",
|
|
10
10
|
"./shared": "./src/shared/index.js",
|
|
11
11
|
"./server/actionIds": "./src/server/actionIds.js",
|
|
12
|
-
"./server": "./src/server/index.js"
|
|
12
|
+
"./server": "./src/server/index.js",
|
|
13
|
+
"./locales/en.json": "./locales/en.json"
|
|
13
14
|
},
|
|
14
15
|
"dependencies": {
|
|
15
|
-
"@jskit-ai/assistant-core": "0.1
|
|
16
|
+
"@jskit-ai/assistant-core": "0.2.1",
|
|
16
17
|
"json-rest-schema": "^1.0.17"
|
|
17
18
|
},
|
|
18
19
|
"peerDependencies": {
|
|
19
|
-
"@jskit-ai/http-runtime": "0.
|
|
20
|
-
"@jskit-ai/http-web": "0.
|
|
21
|
-
"@jskit-ai/kernel": "0.
|
|
22
|
-
"@jskit-ai/realtime": "0.
|
|
23
|
-
"@jskit-ai/shell-web": "0.
|
|
20
|
+
"@jskit-ai/http-runtime": "0.2.0",
|
|
21
|
+
"@jskit-ai/http-web": "0.2.0",
|
|
22
|
+
"@jskit-ai/kernel": "0.2.0",
|
|
23
|
+
"@jskit-ai/realtime": "0.2.0",
|
|
24
|
+
"@jskit-ai/shell-web": "0.2.0",
|
|
24
25
|
"@tanstack/vue-query": "^5.90.5",
|
|
25
26
|
"vue": "^3.5.13",
|
|
26
27
|
"vuetify": "^4.0.0",
|
|
27
|
-
"@jskit-ai/database-runtime": "0.1.
|
|
28
|
+
"@jskit-ai/database-runtime": "0.1.249"
|
|
28
29
|
},
|
|
29
30
|
"description": "Capability-based assistant runtime with per-surface chat, transcripts, settings, and action tools.",
|
|
30
31
|
"jskit": {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { computed, ref, toRef } from "vue";
|
|
3
3
|
import { AssistantConversationElement } from "@jskit-ai/assistant-core/client/conversation";
|
|
4
4
|
import { useAssistantConversation } from "../composables/useAssistantConversation.js";
|
|
5
|
+
import { useJskitText } from "../i18n.js";
|
|
5
6
|
|
|
6
7
|
const props = defineProps({
|
|
7
8
|
conversationId: { type: String, required: true },
|
|
@@ -13,9 +14,10 @@ const props = defineProps({
|
|
|
13
14
|
api: { type: Object, default: null },
|
|
14
15
|
active: { type: Boolean, default: true },
|
|
15
16
|
clearDraftOn: { type: String, default: undefined, validator: value => ["dispatch", "accepted"].includes(value) },
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
// Unset texts follow the active language; an application's own text is shown as given.
|
|
18
|
+
assistantLabel: { type: String, default: undefined },
|
|
19
|
+
welcomeMessage: { type: String, default: undefined },
|
|
20
|
+
placeholder: { type: String, default: undefined },
|
|
19
21
|
layout: { type: String, default: "compact", validator: value => ["page", "compact"].includes(value) },
|
|
20
22
|
data: { type: Object, default: undefined },
|
|
21
23
|
attachments: { type: Object, default: null },
|
|
@@ -26,6 +28,8 @@ const props = defineProps({
|
|
|
26
28
|
avatarSize: { type: String, default: "compact", validator: (value) => ["hidden", "compact", "standard", "large"].includes(value) }
|
|
27
29
|
});
|
|
28
30
|
const emit = defineEmits(["update:avatarSize"]);
|
|
31
|
+
const { t } = useJskitText();
|
|
32
|
+
const assistantLabelText = computed(() => props.assistantLabel ?? t("jskit.assistant.runtime.conversation.assistantLabel"));
|
|
29
33
|
const binding = useAssistantConversation({
|
|
30
34
|
conversationId: toRef(props, "conversationId"), endpoint: toRef(props, "endpoint"),
|
|
31
35
|
surfaceId: toRef(props, "surfaceId"), hostSurfaceId: toRef(props, "hostSurfaceId"),
|
|
@@ -34,8 +38,9 @@ const binding = useAssistantConversation({
|
|
|
34
38
|
data: toRef(props, "data"),
|
|
35
39
|
attachments: toRef(props, "attachments"), suggestions: toRef(props, "suggestions"), models: toRef(props, "models"),
|
|
36
40
|
questions: toRef(props, "questions"), goal: toRef(props, "goal"),
|
|
37
|
-
presentation: computed(() => ({ assistantLabel:
|
|
38
|
-
|
|
41
|
+
presentation: computed(() => ({ assistantLabel: assistantLabelText.value,
|
|
42
|
+
welcomeMessage: props.welcomeMessage ?? t("jskit.assistant.runtime.conversation.welcomeMessage"),
|
|
43
|
+
placeholder: props.placeholder ?? t("jskit.assistant.runtime.conversation.placeholder"), layout: props.layout }))
|
|
39
44
|
});
|
|
40
45
|
const conversation = ref(null);
|
|
41
46
|
defineExpose({ focus: () => conversation.value?.focus(), runtime: binding.runtime });
|
|
@@ -43,7 +48,7 @@ defineExpose({ focus: () => conversation.value?.focus(), runtime: binding.runtim
|
|
|
43
48
|
|
|
44
49
|
<template>
|
|
45
50
|
<AssistantConversationElement
|
|
46
|
-
ref="conversation" :adapter="binding.adapter.value" :label="
|
|
51
|
+
ref="conversation" :adapter="binding.adapter.value" :label="assistantLabelText" :avatar-size="avatarSize"
|
|
47
52
|
class="assistant-client-conversation" @update:avatar-size="emit('update:avatarSize', $event)"
|
|
48
53
|
>
|
|
49
54
|
<template v-for="name in Object.keys($slots).filter(name => name !== 'composer-feedback')" #[name]="scope">
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<AssistantSettingsFormCard
|
|
3
3
|
root-class="assistant-settings-client-element"
|
|
4
|
-
title="
|
|
4
|
+
:title="t('jskit.assistant.runtime.settings.title')"
|
|
5
5
|
:subtitle="subtitle"
|
|
6
|
-
no-permission-message="
|
|
7
|
-
save-label="
|
|
6
|
+
:no-permission-message="t('jskit.assistant.runtime.settings.noPermission')"
|
|
7
|
+
:save-label="t('jskit.assistant.runtime.settings.save')"
|
|
8
8
|
:add-edit="addEdit"
|
|
9
9
|
:show-form-skeleton="showFormSkeleton"
|
|
10
10
|
>
|
|
11
11
|
<v-textarea
|
|
12
12
|
v-model="form.systemPrompt"
|
|
13
|
-
label="
|
|
13
|
+
:label="t('jskit.assistant.runtime.settings.systemPrompt')"
|
|
14
14
|
variant="outlined"
|
|
15
15
|
density="comfortable"
|
|
16
16
|
rows="8"
|
|
@@ -33,6 +33,7 @@ import { useShellWebErrorRuntime } from "@jskit-ai/shell-web/client/error";
|
|
|
33
33
|
import { useSurfaceRouteContext } from "@jskit-ai/shell-web/client/navigation/useSurfaceRouteContext";
|
|
34
34
|
import { resolveAssistantSurfaceConfig } from "../../shared/assistantSurfaces.js";
|
|
35
35
|
import { useWorkspaceWebScopeSupport } from "../support/workspaceScopeSupport.js";
|
|
36
|
+
import { useJskitText } from "../i18n.js";
|
|
36
37
|
|
|
37
38
|
const props = defineProps({
|
|
38
39
|
targetSurfaceId: {
|
|
@@ -49,6 +50,7 @@ const fieldErrors = reactive({
|
|
|
49
50
|
systemPrompt: ""
|
|
50
51
|
});
|
|
51
52
|
|
|
53
|
+
const { fieldErrorMessages, serverMessage, t } = useJskitText();
|
|
52
54
|
const errorRuntime = useShellWebErrorRuntime();
|
|
53
55
|
const queryClient = useQueryClient();
|
|
54
56
|
const appConfig = getClientAppConfig();
|
|
@@ -93,14 +95,15 @@ const settingsApi = createAssistantApi({
|
|
|
93
95
|
|
|
94
96
|
const subtitle = computed(() => {
|
|
95
97
|
if (!assistantSurface.value) {
|
|
96
|
-
return "
|
|
98
|
+
return t("jskit.assistant.runtime.settings.subtitle");
|
|
97
99
|
}
|
|
98
100
|
|
|
101
|
+
const surface = assistantSurface.value.targetSurfaceId;
|
|
99
102
|
if (assistantSurface.value.configScope === "workspace") {
|
|
100
|
-
return
|
|
103
|
+
return t("jskit.assistant.runtime.settings.subtitleWorkspace", { surface });
|
|
101
104
|
}
|
|
102
105
|
|
|
103
|
-
return
|
|
106
|
+
return t("jskit.assistant.runtime.settings.subtitleSurface", { surface });
|
|
104
107
|
});
|
|
105
108
|
|
|
106
109
|
function clearFieldErrors() {
|
|
@@ -151,18 +154,19 @@ const settingsMutation = useMutation({
|
|
|
151
154
|
|
|
152
155
|
const loadError = computed(() => {
|
|
153
156
|
if (!assistantSurface.value) {
|
|
154
|
-
return "
|
|
157
|
+
return t("jskit.assistant.runtime.settings.notConfigured");
|
|
155
158
|
}
|
|
156
159
|
if (hasScope.value) {
|
|
157
|
-
|
|
160
|
+
const failure = settingsQuery.error.value;
|
|
161
|
+
return failure?.message ? normalizeText(serverMessage(failure)) : "";
|
|
158
162
|
}
|
|
159
163
|
|
|
160
164
|
if (assistantSurface.value?.settingsSurfaceRequiresWorkspace) {
|
|
161
165
|
if (workspaceScopeSupport.available !== true) {
|
|
162
|
-
return "
|
|
166
|
+
return t("jskit.assistant.runtime.settings.workspaceUnavailable");
|
|
163
167
|
}
|
|
164
168
|
|
|
165
|
-
return "
|
|
169
|
+
return t("jskit.assistant.runtime.settings.selectWorkspace");
|
|
166
170
|
}
|
|
167
171
|
|
|
168
172
|
return "";
|
|
@@ -179,17 +183,18 @@ async function submit() {
|
|
|
179
183
|
}
|
|
180
184
|
});
|
|
181
185
|
if (!validation.ok) {
|
|
182
|
-
fieldErrors.systemPrompt = String(validation.
|
|
186
|
+
fieldErrors.systemPrompt = String(fieldErrorMessages(validation).systemPrompt || "");
|
|
183
187
|
return;
|
|
184
188
|
}
|
|
185
189
|
|
|
186
190
|
try {
|
|
187
191
|
await settingsMutation.mutateAsync(validation.value);
|
|
188
192
|
} catch (error) {
|
|
189
|
-
const nextFieldErrors = normalizeObject(error?.details?.fieldErrors)
|
|
193
|
+
const nextFieldErrors = fieldErrorMessages({ fieldErrors: normalizeObject(error?.details?.fieldErrors),
|
|
194
|
+
fieldErrorCodes: error?.fieldErrorCodes || error?.details?.fieldErrorCodes });
|
|
190
195
|
fieldErrors.systemPrompt = String(nextFieldErrors.systemPrompt || "");
|
|
191
196
|
reportAssistantSettingsError(
|
|
192
|
-
|
|
197
|
+
(error?.message && normalizeText(serverMessage(error))) || t("jskit.assistant.runtime.settings.updateFailed"),
|
|
193
198
|
"assistant.settings:save"
|
|
194
199
|
);
|
|
195
200
|
}
|
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
import { computed, onMounted, onScopeDispose, reactive, ref, toRef, watch, watchPostEffect } from "vue";
|
|
3
3
|
import { AssistantConversationElement } from "@jskit-ai/assistant-core/client/conversation";
|
|
4
4
|
import { useAssistantRuntime } from "../composables/useAssistantRuntime.js";
|
|
5
|
-
import { mapAssistantConversationTurns } from "../support/assistantRuntimeState.js";
|
|
5
|
+
import { mapAssistantConversationTurns, toolStatusText } from "../support/assistantRuntimeState.js";
|
|
6
|
+
import { DEFAULT_CONVERSATION_TITLE } from "../../shared/assistantRuntimeConfig.js";
|
|
7
|
+
import { useJskitText } from "../i18n.js";
|
|
6
8
|
|
|
7
9
|
const props = defineProps({
|
|
8
10
|
surfaceId: { type: String, required: true },
|
|
9
11
|
layout: { type: String, default: "page", validator: (value) => ["page", "compact"].includes(value) },
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
// Unset texts follow the active language; an application's own text is shown as given.
|
|
13
|
+
assistantLabel: { type: String, default: undefined },
|
|
14
|
+
welcomeMessage: { type: String, default: undefined },
|
|
15
|
+
placeholder: { type: String, default: undefined },
|
|
13
16
|
showToolActivity: { type: Boolean, default: true },
|
|
14
17
|
connections: { type: Array, default: () => [] },
|
|
15
18
|
attachments: { type: Object, default: null },
|
|
@@ -19,6 +22,8 @@ const props = defineProps({
|
|
|
19
22
|
configurationMode: { type: String, default: "editable", validator: value => ["hidden", "readonly", "editable"].includes(value) }
|
|
20
23
|
});
|
|
21
24
|
const integrationId = defineModel("integrationId", { type: String, default: "" });
|
|
25
|
+
const { t } = useJskitText();
|
|
26
|
+
const assistantLabelText = computed(() => props.assistantLabel ?? t("jskit.assistant.runtime.conversation.assistantLabel"));
|
|
22
27
|
const draftIntegrationId = ref(integrationId.value);
|
|
23
28
|
watch(integrationId, value => { draftIntegrationId.value = value; });
|
|
24
29
|
const runtime = useAssistantRuntime({ surfaceId: toRef(props, "surfaceId"), integrationId });
|
|
@@ -49,9 +54,10 @@ const adapter = computed(() => ({
|
|
|
49
54
|
goal: props.goal,
|
|
50
55
|
activity: props.activity,
|
|
51
56
|
models: props.connections.length && props.configurationMode !== "hidden" ? {
|
|
52
|
-
providerRows: [{ id: "application", label: "
|
|
57
|
+
providerRows: [{ id: "application", label: t("jskit.assistant.runtime.connection.applicationAi") }],
|
|
53
58
|
modelProviderId: "application", modelRows: props.connections, modelId: draftIntegrationId.value,
|
|
54
|
-
selectionSummary: props.connections.find(connection => connection.id === integrationId.value)?.label ||
|
|
59
|
+
selectionSummary: props.connections.find(connection => connection.id === integrationId.value)?.label ||
|
|
60
|
+
t("jskit.assistant.runtime.connection.applicationDefault"),
|
|
55
61
|
changesDisabled: props.configurationMode !== "editable" || state.isStreaming,
|
|
56
62
|
canSave: props.configurationMode === "editable" && !state.isStreaming && draftIntegrationId.value !== integrationId.value &&
|
|
57
63
|
props.connections.some(connection => connection.id === draftIntegrationId.value),
|
|
@@ -63,8 +69,9 @@ const adapter = computed(() => ({
|
|
|
63
69
|
scrollKey: `${state.scopeKey}:${state.activeConversationId || "new"}`,
|
|
64
70
|
loading: state.isRestoringConversation,
|
|
65
71
|
working: state.isStreaming && !runtime.delivery.state.sending,
|
|
66
|
-
assistantLabel:
|
|
67
|
-
welcomeMessage: state.messages.length || state.isRestoringConversation ? ""
|
|
72
|
+
assistantLabel: assistantLabelText.value,
|
|
73
|
+
welcomeMessage: state.messages.length || state.isRestoringConversation ? ""
|
|
74
|
+
: props.welcomeMessage ?? t("jskit.assistant.runtime.conversation.welcomeMessage")
|
|
68
75
|
},
|
|
69
76
|
composer: {
|
|
70
77
|
draft: state.input,
|
|
@@ -73,7 +80,7 @@ const adapter = computed(() => ({
|
|
|
73
80
|
disabled: !state.hasRuntimeScope,
|
|
74
81
|
canStop: state.isStreaming,
|
|
75
82
|
stopPending: state.isCanceling,
|
|
76
|
-
placeholder: props.placeholder,
|
|
83
|
+
placeholder: props.placeholder ?? t("jskit.assistant.runtime.conversation.placeholder"),
|
|
77
84
|
rows: 2,
|
|
78
85
|
density: props.layout === "compact" ? "compact" : "default",
|
|
79
86
|
submitOnEnter: true,
|
|
@@ -97,9 +104,18 @@ function submitMessage({ attachments = [], retryMessageId = "" }) {
|
|
|
97
104
|
});
|
|
98
105
|
}
|
|
99
106
|
|
|
107
|
+
const CONVERSATION_STATUS_KEY = "jskit.assistant.runtime.conversation.status.";
|
|
108
|
+
|
|
109
|
+
// The saved default title is stored in English; show it in the active language.
|
|
110
|
+
function conversationTitle(entry, fallbackKey) {
|
|
111
|
+
if (entry?.title === DEFAULT_CONVERSATION_TITLE) return t("jskit.assistant.runtime.surface.newConversation");
|
|
112
|
+
return entry?.title || t(fallbackKey);
|
|
113
|
+
}
|
|
114
|
+
|
|
100
115
|
function conversationSubtitle(entry) {
|
|
101
|
-
return [runtime.meta.normalizeConversationStatus(entry.status)
|
|
102
|
-
|
|
116
|
+
return [t(CONVERSATION_STATUS_KEY + runtime.meta.normalizeConversationStatus(entry.status)),
|
|
117
|
+
runtime.meta.formatConversationStartedAt(entry.startedAt),
|
|
118
|
+
t("jskit.assistant.runtime.surface.messageCount", { count: Number(entry.messageCount || 0) })].join(" · ");
|
|
103
119
|
}
|
|
104
120
|
|
|
105
121
|
async function selectConversation(entry) {
|
|
@@ -116,25 +132,25 @@ defineExpose({ focus: () => conversation.value?.focus() });
|
|
|
116
132
|
</script>
|
|
117
133
|
|
|
118
134
|
<template>
|
|
119
|
-
<section ref="root" class="assistant-surface" :class="`assistant-surface--${layout}`" :aria-label="
|
|
135
|
+
<section ref="root" class="assistant-surface" :class="`assistant-surface--${layout}`" :aria-label="assistantLabelText">
|
|
120
136
|
<div class="assistant-surface__toolbar">
|
|
121
|
-
<span class="assistant-surface__title">{{ selectedConversation
|
|
122
|
-
<v-btn variant="text" @click="historyOpen = true">
|
|
137
|
+
<span class="assistant-surface__title">{{ conversationTitle(selectedConversation, "jskit.assistant.runtime.surface.newConversation") }}</span>
|
|
138
|
+
<v-btn variant="text" @click="historyOpen = true">{{ t("jskit.assistant.runtime.surface.conversations") }}</v-btn>
|
|
123
139
|
<v-menu v-if="showToolActivity && state.pendingToolEvents.length" location="bottom end">
|
|
124
140
|
<template #activator="{ props: menuProps }">
|
|
125
|
-
<v-btn v-bind="menuProps" variant="text">
|
|
141
|
+
<v-btn v-bind="menuProps" variant="text">{{ t("jskit.assistant.runtime.surface.activity") }}</v-btn>
|
|
126
142
|
</template>
|
|
127
|
-
<v-list aria-label="
|
|
128
|
-
<v-list-item v-for="event in state.pendingToolEvents" :key="event.id" :title="event.name" :subtitle="event.status" />
|
|
143
|
+
<v-list :aria-label="t('jskit.assistant.runtime.surface.toolActivity')" max-height="320" max-width="360" class="assistant-surface__activity">
|
|
144
|
+
<v-list-item v-for="event in state.pendingToolEvents" :key="event.id" :title="event.name" :subtitle="toolStatusText(event.status)" />
|
|
129
145
|
</v-list>
|
|
130
146
|
</v-menu>
|
|
131
147
|
</div>
|
|
132
|
-
<AssistantConversationElement ref="conversation" :adapter="adapter" label="
|
|
148
|
+
<AssistantConversationElement ref="conversation" :adapter="adapter" :label="t('jskit.assistant.runtime.surface.conversation')" class="assistant-surface__conversation">
|
|
133
149
|
<template v-if="$slots.activity" #activity="scope"><slot name="activity" v-bind="scope" /></template>
|
|
134
150
|
<template v-if="$slots.attachments" #attachments="scope"><slot name="attachments" v-bind="scope" /></template>
|
|
135
151
|
<template #message-actions="{ message }">
|
|
136
152
|
<p v-if="message.status === 'failed' && message.error" class="assistant-surface__message-error" role="status">{{ message.error }}</p>
|
|
137
|
-
<p v-if="message.status === 'interrupted'" class="assistant-surface__notice" role="status">
|
|
153
|
+
<p v-if="message.status === 'interrupted'" class="assistant-surface__notice" role="status">{{ t("jskit.assistant.runtime.surface.responseCanceled") }}</p>
|
|
138
154
|
</template>
|
|
139
155
|
<template #composer-feedback>
|
|
140
156
|
<p v-if="state.error && !state.messages.some((message) => message.error === state.error)" class="assistant-surface__message-error" role="status">{{ state.error }}</p>
|
|
@@ -142,28 +158,35 @@ defineExpose({ focus: () => conversation.value?.focus() });
|
|
|
142
158
|
<template #composer-tools><slot name="composer-tools" :runtime="runtime" /></template>
|
|
143
159
|
</AssistantConversationElement>
|
|
144
160
|
|
|
145
|
-
<v-dialog v-model="historyOpen" max-width="640" scrollable aria-label="
|
|
161
|
+
<v-dialog v-model="historyOpen" max-width="640" scrollable :aria-label="t('jskit.assistant.runtime.surface.conversations')">
|
|
146
162
|
<v-card class="assistant-surface__history">
|
|
147
|
-
<v-card-title>
|
|
163
|
+
<v-card-title>{{ t("jskit.assistant.runtime.surface.conversations") }}</v-card-title>
|
|
148
164
|
<v-card-actions class="assistant-surface__history-actions">
|
|
149
|
-
<v-btn color="primary" :disabled="!state.canStartNewConversation" @click="startNewConversation">
|
|
150
|
-
|
|
151
|
-
|
|
165
|
+
<v-btn color="primary" :disabled="!state.canStartNewConversation" @click="startNewConversation">
|
|
166
|
+
{{ t("jskit.assistant.runtime.surface.startNewConversation") }}
|
|
167
|
+
</v-btn>
|
|
168
|
+
<v-btn :disabled="state.conversationHistoryLoading" @click="runtime.actions.refreshConversationHistory">
|
|
169
|
+
{{ t("jskit.assistant.runtime.surface.refresh") }}
|
|
170
|
+
</v-btn>
|
|
171
|
+
<v-btn @click="historyOpen = false">{{ t("jskit.assistant.runtime.surface.close") }}</v-btn>
|
|
152
172
|
</v-card-actions>
|
|
153
173
|
<v-card-text>
|
|
154
174
|
<p v-if="state.conversationHistoryError" role="alert">{{ state.conversationHistoryError }}</p>
|
|
155
|
-
<v-skeleton-loader
|
|
156
|
-
|
|
157
|
-
|
|
175
|
+
<v-skeleton-loader
|
|
176
|
+
v-if="state.conversationHistoryLoading && !state.conversationHistory.length" type="list-item-two-line@3"
|
|
177
|
+
:aria-label="t('jskit.assistant.runtime.surface.loadingConversationsLabel')"
|
|
178
|
+
/>
|
|
179
|
+
<v-list v-else :aria-label="t('jskit.assistant.runtime.surface.savedConversations')">
|
|
180
|
+
<v-list-item v-if="!state.conversationHistory.length" :title="t('jskit.assistant.runtime.surface.noSavedConversations')" />
|
|
158
181
|
<v-list-item
|
|
159
182
|
v-for="entry in state.conversationHistory" :key="entry.id"
|
|
160
183
|
:active="String(entry.id) === state.activeConversationId"
|
|
161
|
-
:title="entry
|
|
184
|
+
:title="conversationTitle(entry, 'jskit.assistant.runtime.surface.untitledConversation')" :subtitle="conversationSubtitle(entry)"
|
|
162
185
|
:disabled="state.isStreaming || state.isRestoringConversation" @click="selectConversation(entry)"
|
|
163
186
|
/>
|
|
164
187
|
</v-list>
|
|
165
188
|
<v-btn v-if="state.conversationHistoryHasMore" :disabled="state.conversationHistoryLoadingMore" variant="text" @click="runtime.actions.loadMoreConversationHistory">
|
|
166
|
-
{{ state.conversationHistoryLoadingMore ?
|
|
189
|
+
{{ t(state.conversationHistoryLoadingMore ? "jskit.assistant.runtime.surface.loadingConversations" : "jskit.assistant.runtime.surface.loadOlderConversations") }}
|
|
167
190
|
</v-btn>
|
|
168
191
|
</v-card-text>
|
|
169
192
|
</v-card>
|