@jskit-ai/assistant-core 0.1.199 → 0.1.201

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 (164) hide show
  1. package/examples/conversation/README.md +8 -1
  2. package/examples/conversation/package.json +5 -5
  3. package/fixtures/responsive-assistant/App.vue +4 -1
  4. package/package.json +99 -36
  5. package/src/client/conversation/AssistantConversationElement.vue +27 -4
  6. package/src/client/conversation/AssistantConversationStatus.vue +24 -0
  7. package/src/client/conversation/AssistantGoalControl.vue +8 -5
  8. package/src/client/conversation/AssistantTranscript.vue +10 -3
  9. package/src/client/conversation/index.js +2 -0
  10. package/src/client/conversation/messageDelivery.js +115 -32
  11. package/src/client/conversation/retainedConversation.js +32 -0
  12. package/src/client/conversation/useAssistantQuestions.js +76 -0
  13. package/src/client/lib/assistantApi.js +72 -2
  14. package/src/server/conversation/agentRun.js +222 -0
  15. package/src/server/conversation/attachments.js +93 -0
  16. package/src/server/conversation/claudeClient.js +221 -0
  17. package/src/server/conversation/claudeHistory.js +256 -0
  18. package/src/server/conversation/claudeProcess.js +327 -0
  19. package/src/server/conversation/claudeTools.js +42 -0
  20. package/src/server/conversation/claudeTurn.js +1082 -0
  21. package/src/server/conversation/codexConfiguration.js +1183 -0
  22. package/src/server/conversation/codexConversations.js +1082 -0
  23. package/src/server/conversation/codexDetachedTurn.js +332 -0
  24. package/src/server/conversation/codexEvents.js +194 -1
  25. package/src/server/conversation/codexGoalCommands.js +124 -0
  26. package/src/server/conversation/codexHelperExecution.js +306 -0
  27. package/src/server/conversation/codexHelperThreadLedger.js +804 -0
  28. package/src/server/conversation/codexHelperThreadRecovery.js +395 -0
  29. package/src/server/conversation/codexHelperThreads.js +638 -0
  30. package/src/server/conversation/codexHistoryAdapter.js +369 -0
  31. package/src/server/conversation/codexMessageCommands.js +663 -0
  32. package/src/server/conversation/codexMessageDelivery.js +211 -0
  33. package/src/server/conversation/codexNativeHistoryExport.js +95 -0
  34. package/src/server/conversation/codexNotificationObserver.js +482 -0
  35. package/src/server/conversation/codexNotificationQueue.js +103 -0
  36. package/src/server/conversation/codexObservationControl.js +174 -0
  37. package/src/server/conversation/codexOutput.js +931 -0
  38. package/src/server/conversation/codexProcess.js +1559 -0
  39. package/src/server/conversation/codexProcessLeader.js +14 -0
  40. package/src/server/conversation/codexProvider.js +2710 -0
  41. package/src/server/conversation/codexProviderOwner.js +1058 -0
  42. package/src/server/conversation/codexProviderSelection.js +218 -0
  43. package/src/server/conversation/codexReasoning.js +314 -0
  44. package/src/server/conversation/codexRenewalCommands.js +290 -0
  45. package/src/server/conversation/codexResultSettlement.js +409 -0
  46. package/src/server/conversation/codexRunJournal.js +658 -0
  47. package/src/server/conversation/codexRuntimeLifecycle.js +288 -0
  48. package/src/server/conversation/codexSessionCleanup.js +96 -0
  49. package/src/server/conversation/codexSessionReconciliation.js +284 -0
  50. package/src/server/conversation/codexThreadReadiness.js +397 -0
  51. package/src/server/conversation/codexThreadStatus.js +354 -0
  52. package/src/server/conversation/codexTurn.js +701 -255
  53. package/src/server/conversation/codexTurnControl.js +317 -0
  54. package/src/server/conversation/codexTurnRecovery.js +990 -0
  55. package/src/server/conversation/codexTurnState.js +549 -0
  56. package/src/server/conversation/commandHook.js +18 -0
  57. package/src/server/conversation/commandWrapper.js +37 -0
  58. package/src/server/conversation/configuration.js +43 -0
  59. package/src/server/conversation/continuity.js +289 -0
  60. package/src/server/conversation/fileStorage.js +55 -0
  61. package/src/server/conversation/hookBridge.js +19 -0
  62. package/src/server/conversation/index.js +13 -0
  63. package/src/server/conversation/localExecution.js +665 -0
  64. package/src/server/conversation/memoryStorage.js +7 -72
  65. package/src/server/conversation/nativeHistoryExport.js +90 -0
  66. package/src/server/conversation/nativeStorage.js +23 -0
  67. package/src/server/conversation/openCodeClient.js +209 -19
  68. package/src/server/conversation/openCodePlugin.js +84 -0
  69. package/src/server/conversation/openCodeProcess.js +476 -0
  70. package/src/server/conversation/openCodeRuntime.js +1578 -0
  71. package/src/server/conversation/openCodeTools.js +60 -0
  72. package/src/server/conversation/openCodeTurn.js +557 -0
  73. package/src/server/conversation/provider.js +30 -0
  74. package/src/server/conversation/providers/api.js +136 -0
  75. package/src/server/conversation/providers/claude.js +63 -0
  76. package/src/server/conversation/providers/claudeDriver.js +425 -0
  77. package/src/server/conversation/providers/codex.js +248 -0
  78. package/src/server/conversation/providers/codexDriver.js +1078 -0
  79. package/src/server/conversation/providers/opencode.js +49 -0
  80. package/src/server/conversation/providers/opencodeDriver.js +650 -0
  81. package/src/server/conversation/providers/scoped.js +312 -0
  82. package/src/server/conversation/runtime.js +2220 -0
  83. package/src/server/conversation/runtimeStateUpgrade.js +125 -0
  84. package/src/server/conversation/storage.js +184 -0
  85. package/src/server/conversation/streams.js +7 -2
  86. package/src/server/conversation/structuredOutput.js +240 -0
  87. package/src/server/conversation/systemPrompt.js +54 -0
  88. package/src/server/conversation/tools.js +84 -0
  89. package/src/server/conversation/transcript.js +5 -4
  90. package/src/server/hosts/claudeHistory.js +5 -0
  91. package/src/server/hosts/claudeProcess.js +11 -0
  92. package/src/server/hosts/claudeTurn.js +4 -0
  93. package/src/server/hosts/codexConfiguration.js +20 -0
  94. package/src/server/hosts/codexEvents.js +13 -0
  95. package/src/server/hosts/codexProcess.js +12 -0
  96. package/src/server/hosts/codexProvider.js +23 -0
  97. package/src/server/hosts/codexTurn.js +20 -0
  98. package/src/server/hosts/nativeHistory.js +3 -0
  99. package/src/server/hosts/openCodeClient.js +3 -0
  100. package/src/server/hosts/openCodeProcess.js +23 -0
  101. package/src/server/hosts/openCodeTurn.js +7 -0
  102. package/src/server/index.js +2 -0
  103. package/src/server/lib/aiConnectionClient.js +13 -6
  104. package/src/server/lib/assistantCompletion.js +190 -0
  105. package/src/server/lib/assistantToolLoop.js +314 -0
  106. package/src/server/lib/serviceToolCatalog.js +25 -10
  107. package/src/shared/assistantResponseText.js +29 -0
  108. package/src/shared/conversation/attachments.js +19 -0
  109. package/src/shared/conversation/index.js +3 -0
  110. package/src/shared/conversation/pagination.js +78 -0
  111. package/src/shared/conversation/patches.js +150 -0
  112. package/src/shared/conversation/questions.js +48 -0
  113. package/src/shared/conversation/streaming.js +7 -4
  114. package/src/shared/index.js +2 -0
  115. package/src/shared/nativeProviders.js +45 -0
  116. package/src/testing/conversationStorageContract.js +31 -1
  117. package/src/testing/nativeCodex.js +11 -0
  118. package/src/testing/nativeOpenCode.js +3 -0
  119. package/test/aiConnectionClient.test.js +2 -0
  120. package/test/apiConversationDriver.test.js +73 -0
  121. package/test/assistantApiSurfaceHeader.test.js +85 -0
  122. package/test/assistantBoundedToolLoop.test.js +285 -0
  123. package/test/assistantScroll.browser.test.js +16 -0
  124. package/test/claudeClient.test.js +189 -0
  125. package/test/claudeConversationSession.test.js +1173 -0
  126. package/test/claudeInstructionResume.test.js +82 -0
  127. package/test/claudeProcess.test.js +204 -0
  128. package/test/claudeTurn.test.js +253 -0
  129. package/test/codexConversationSession.test.js +2122 -0
  130. package/test/codexGoal.test.js +37 -0
  131. package/test/codexHelperDeletion.test.js +163 -0
  132. package/test/codexHelperThreadLedger.test.js +494 -0
  133. package/test/codexHistoryAdapter.test.js +541 -0
  134. package/test/codexInteractiveArguments.test.js +30 -0
  135. package/test/codexIsolation.test.js +756 -0
  136. package/test/codexModelCatalog.test.js +362 -0
  137. package/test/codexNativeHistoryExport.test.js +81 -0
  138. package/test/codexThreadSelection.test.js +136 -0
  139. package/test/codexTurn.test.js +2158 -0
  140. package/test/conversation.test.js +110 -0
  141. package/test/conversationAgentRun.test.js +326 -0
  142. package/test/conversationApiFacilities.test.js +90 -0
  143. package/test/conversationAttachments.test.js +238 -0
  144. package/test/conversationCommandWrapper.test.js +48 -0
  145. package/test/conversationConfiguration.test.js +174 -0
  146. package/test/conversationNativeStorage.test.js +25 -0
  147. package/test/conversationReplacement.test.js +172 -0
  148. package/test/conversationRuntime.test.js +241 -0
  149. package/test/conversationRuntimeStateUpgrade.test.js +144 -0
  150. package/test/conversationSession.test.js +1080 -0
  151. package/test/conversationStreaming.test.js +68 -0
  152. package/test/conversationSystemPrompt.test.js +84 -0
  153. package/test/conversationTools.test.js +416 -0
  154. package/test/fileConversationStorage.test.js +73 -0
  155. package/test/fixtures/codexHelperThreadLedger.js +39 -0
  156. package/test/localConversationExecution.test.js +90 -0
  157. package/test/messageDelivery.test.js +234 -1
  158. package/test/nativeConversationRetirement.test.js +215 -0
  159. package/test/openCodeClient.test.js +515 -0
  160. package/test/openCodeProcess.test.js +177 -0
  161. package/test/openCodeRuntime.test.js +322 -0
  162. package/test/openCodeTools.test.js +287 -0
  163. package/test/opencodeConversationSession.test.js +789 -0
  164. package/test/serviceToolCatalog.test.js +24 -0
@@ -1,4 +1,11 @@
1
- # Standalone conversation example
1
+ # Conversation primitives example
2
+
3
+ This lower-level example assembles UI, delivery and HTTP primitives. For a new
4
+ application, start with `@jskit-ai/assistant-voice/examples/conversation`; speech
5
+ is optional, and its five chats plus headless task use the supplied conversation
6
+ runtime, server integration and retained client binding. Native engines use that
7
+ same ordinary integration. This example remains useful for custom primitive
8
+ composition and its existing storage/attachment contract.
2
9
 
3
10
  Copy this directory into a new application, then run:
4
11
 
@@ -8,14 +8,14 @@
8
8
  "build": "vite build"
9
9
  },
10
10
  "dependencies": {
11
- "@jskit-ai/assistant-core": "0.1.199",
12
- "@jskit-ai/http-runtime": "0.1.219",
13
- "@jskit-ai/kernel": "0.1.221",
11
+ "@jskit-ai/assistant-core": "0.1.201",
12
+ "@jskit-ai/http-runtime": "0.1.221",
13
+ "@jskit-ai/kernel": "0.1.223",
14
14
  "@mdi/js": "^7.4.47",
15
15
  "vue": "^3.5.13",
16
16
  "vuetify": "^4.0.0",
17
- "@jskit-ai/connectors-core": "0.1.38",
18
- "@jskit-ai/connectors-catalog": "0.1.38"
17
+ "@jskit-ai/connectors-core": "0.1.40",
18
+ "@jskit-ai/connectors-catalog": "0.1.40"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@vitejs/plugin-vue": "^6.0.7",
@@ -4,6 +4,7 @@ import AssistantConversationElement from "../../src/client/conversation/Assistan
4
4
  import { conversationTurnsFromMessages } from "../../src/shared/conversation/turns.js";
5
5
  import { mergeConversationStream } from "../../src/shared/conversation/streaming.js";
6
6
  const draft = ref("");
7
+ const visible = ref(true);
7
8
  const query = new URLSearchParams(location.search);
8
9
  const controls = query.has("controls");
9
10
  const deferredHistory = query.has("history");
@@ -50,6 +51,7 @@ function finishHistory(error = "") {
50
51
  }
51
52
  const adapter = computed(() => ({
52
53
  conversation: {
54
+ visible: visible.value,
53
55
  turns: mergeConversationStream(conversationTurnsFromMessages(messages.value), { messages: [liveMessage.value] }), scrollKey: scope.value,
54
56
  hasMoreBefore: hasMoreBefore.value, loadingMore: loadingMore.value, loadMoreError: loadMoreError.value
55
57
  },
@@ -76,6 +78,7 @@ const adapter = computed(() => ({
76
78
  <button @click="finishStream">Finish answer</button>
77
79
  </nav>
78
80
  <nav v-if="controls">
81
+ <button @click="visible = !visible">Toggle visibility</button>
79
82
  <button @click="scope = scope === 'one' ? 'two' : 'one'">Change conversation</button>
80
83
  <button @click="messages.push({ id: `added-${messages.length}`, role: 'assistant', text: 'New reply. '.repeat(30), status: 'completed' })">Append reply</button>
81
84
  </nav>
@@ -85,7 +88,7 @@ const adapter = computed(() => ({
85
88
  <button @click="hasMoreBefore = false">Exhaust history</button>
86
89
  <output>History requests: {{ historyRequests }}</output>
87
90
  </nav>
88
- <AssistantConversationElement :adapter="adapter" class="assistant-responsive-fixture__conversation" />
91
+ <AssistantConversationElement v-show="visible" :adapter="adapter" class="assistant-responsive-fixture__conversation" />
89
92
  </main>
90
93
  </v-main>
91
94
  </v-app>
package/package.json CHANGED
@@ -1,27 +1,38 @@
1
1
  {
2
2
  "name": "@jskit-ai/assistant-core",
3
- "version": "0.1.199",
3
+ "version": "0.1.201",
4
4
  "type": "module",
5
5
  "scripts": {
6
- "test": "node --test"
6
+ "test": "node --test --test-concurrency=1"
7
7
  },
8
8
  "exports": {
9
9
  "./client": "./src/client/index.js",
10
10
  "./server": "./src/server/index.js",
11
11
  "./shared": "./src/shared/index.js",
12
12
  "./client/conversation": "./src/client/conversation/index.js",
13
- "./server/codex-events": "./src/server/conversation/codexEvents.js",
14
- "./server/codex-turn": "./src/server/conversation/codexTurn.js",
15
- "./server/codex-client": "./src/server/conversation/codexClient.js",
16
- "./server/opencode-client": "./src/server/conversation/openCodeClient.js",
13
+ "./server/codex-events": "./src/server/hosts/codexEvents.js",
14
+ "./server/codex-turn": "./src/server/hosts/codexTurn.js",
15
+ "./server/opencode-client": "./src/server/hosts/openCodeClient.js",
17
16
  "./server/conversation": "./src/server/conversation/index.js",
18
17
  "./shared/conversation": "./src/shared/conversation/index.js",
19
- "./client/conversation-submit": "./src/client/conversation/submitText.js",
20
18
  "./client/conversation-scroll": "./src/client/conversation/useScrollToBottom.js",
21
19
  "./testing/conversation-storage": "./src/testing/conversationStorageContract.js",
22
20
  "./client/conversation-suggestions": "./src/client/conversation/useAssistantSuggestions.js",
21
+ "./client/conversation-questions": "./src/client/conversation/useAssistantQuestions.js",
23
22
  "./client/conversation-attachments": "./src/client/conversation/useAssistantAttachments.js",
24
- "./client/conversation-delivery": "./src/client/conversation/messageDelivery.js"
23
+ "./client/conversation-delivery": "./src/client/conversation/messageDelivery.js",
24
+ "./server/claude-history": "./src/server/hosts/claudeHistory.js",
25
+ "./server/claude-process": "./src/server/hosts/claudeProcess.js",
26
+ "./server/claude-turn": "./src/server/hosts/claudeTurn.js",
27
+ "./server/codex-configuration": "./src/server/hosts/codexConfiguration.js",
28
+ "./server/codex-process": "./src/server/hosts/codexProcess.js",
29
+ "./server/opencode-turn": "./src/server/hosts/openCodeTurn.js",
30
+ "./server/opencode-process": "./src/server/hosts/openCodeProcess.js",
31
+ "./shared/native-providers": "./src/shared/nativeProviders.js",
32
+ "./server/codex-provider": "./src/server/hosts/codexProvider.js",
33
+ "./server/native-history": "./src/server/hosts/nativeHistory.js",
34
+ "./testing/native-codex": "./src/testing/nativeCodex.js",
35
+ "./testing/native-opencode": "./src/testing/nativeOpenCode.js"
25
36
  },
26
37
  "dependencies": {
27
38
  "@ai-sdk/anthropic": "^4.0.53",
@@ -34,8 +45,8 @@
34
45
  "@ai-sdk/perplexity": "^4.0.43",
35
46
  "@ai-sdk/togetherai": "^3.0.49",
36
47
  "@ai-sdk/xai": "^4.0.58",
37
- "@jskit-ai/resource-core": "0.1.163",
38
- "@jskit-ai/resource-crud-core": "0.1.163",
48
+ "@jskit-ai/resource-core": "0.1.165",
49
+ "@jskit-ai/resource-crud-core": "0.1.165",
39
50
  "@mdi/js": "^7.4.47",
40
51
  "@openrouter/ai-sdk-provider": "^3.0.0",
41
52
  "ai": "^7.0.101",
@@ -44,8 +55,8 @@
44
55
  "ws": "^8.20.1"
45
56
  },
46
57
  "peerDependencies": {
47
- "@jskit-ai/http-runtime": "0.1.219",
48
- "@jskit-ai/kernel": "0.1.221",
58
+ "@jskit-ai/http-runtime": "0.1.221",
59
+ "@jskit-ai/kernel": "0.1.223",
49
60
  "vue": "^3.5.13",
50
61
  "vuetify": "^4.0.0"
51
62
  },
@@ -69,61 +80,113 @@
69
80
  "metadata": {
70
81
  "apiSummary": {
71
82
  "surfaces": [
83
+ {
84
+ "subpath": "./server/conversation",
85
+ "summary": "Conversation runtime: createConversationRuntime and file or application-owned storage; advanced storage and host contracts are documented separately."
86
+ },
87
+ {
88
+ "subpath": "./client/conversation",
89
+ "summary": "Optional UI: Embeddable conversation element, transcript, composer and progress presentation with application-owned state and actions."
90
+ },
91
+ {
92
+ "subpath": "./shared/conversation",
93
+ "summary": "Optional UI: Turn mapping, rich-text parsing, question helpers and scroll-follow state."
94
+ },
95
+ {
96
+ "subpath": "./client/conversation-scroll",
97
+ "summary": "Optional UI: Layout-aware scroll-follow operations."
98
+ },
99
+ {
100
+ "subpath": "./client/conversation-suggestions",
101
+ "summary": "Optional UI: Suggestion state and configuration for an application-supplied, separately authorized generator."
102
+ },
103
+ {
104
+ "subpath": "./client/conversation-questions",
105
+ "summary": "Optional UI: Question extraction and answer selection used by the supplied binding and custom presentation."
106
+ },
107
+ {
108
+ "subpath": "./client/conversation-attachments",
109
+ "summary": "Optional UI: Attachment upload and selection state; applications retain storage and authorization."
110
+ },
111
+ {
112
+ "subpath": "./client/conversation-delivery",
113
+ "summary": "Optional UI: Optimistic message delivery, immutable retry payloads, failure actions and canonical receipt reconciliation."
114
+ },
72
115
  {
73
116
  "subpath": "./client",
74
- "summary": "Exports reusable assistant UI primitives, HTTP helpers, and client API builders."
117
+ "summary": "Framework integration: Exports reusable assistant UI primitives, HTTP helpers, and client API builders."
75
118
  },
76
119
  {
77
120
  "subpath": "./server",
78
- "summary": "Exports reusable assistant AI/provider and NDJSON streaming helpers."
121
+ "summary": "Framework integration: Exports reusable assistant AI/provider and NDJSON streaming helpers."
79
122
  },
80
123
  {
81
124
  "subpath": "./shared",
82
- "summary": "Exports reusable assistant validators, path helpers, query keys, and stream/settings events."
125
+ "summary": "Framework integration: Exports reusable assistant validators, path helpers, query keys, and stream/settings events."
83
126
  },
84
127
  {
85
- "subpath": "./client/conversation",
86
- "summary": "Embeddable conversation element, transcript, composer and progress presentation with application-owned state and actions."
128
+ "subpath": "./server/codex-events",
129
+ "summary": "Advanced native host: Codex event identities, thread-history projections and context-refresh facts for native hosts."
87
130
  },
88
131
  {
89
- "subpath": "./server/conversation",
90
- "summary": "Conversation transcript lifecycle with replaceable application storage and an explicit transient memory adapter."
132
+ "subpath": "./server/codex-turn",
133
+ "summary": "Advanced native host: Original Codex run, receipt, observation, goal, steering, control, scoped-turn and renewal coordination."
91
134
  },
92
135
  {
93
- "subpath": "./shared/conversation",
94
- "summary": "Turn mapping, rich-text parsing, question helpers and scroll-follow state."
136
+ "subpath": "./server/opencode-client",
137
+ "summary": "Advanced native host: OpenCode native history retirement for existing hosts."
95
138
  },
96
139
  {
97
- "subpath": "./server/codex-client",
98
- "summary": "Codex app-server JSON-RPC client; the application supplies the endpoint, identity and request authorization."
140
+ "subpath": "./server/claude-history",
141
+ "summary": "Advanced native host: Claude native transcript reading, storage inventory and authorized history retirement."
99
142
  },
100
143
  {
101
- "subpath": "./server/codex-turn",
102
- "summary": "Native Codex detached-turn observation and final-result recovery."
144
+ "subpath": "./server/claude-process",
145
+ "summary": "Advanced native host: Claude command and account-query configuration, owned process startup and verified cleanup."
103
146
  },
104
147
  {
105
- "subpath": "./server/codex-events",
106
- "summary": "Native Codex notification and thread-history classification."
148
+ "subpath": "./server/claude-turn",
149
+ "summary": "Advanced native host: Original Claude admission, streaming, background-task completion, goals, steering and private renewal execution."
107
150
  },
108
151
  {
109
- "subpath": "./server/opencode-client",
110
- "summary": "Bounded OpenCode loopback HTTP and SSE client."
152
+ "subpath": "./server/codex-configuration",
153
+ "summary": "Advanced native host: Codex model and account configuration with verified Helper isolation for existing native hosts."
111
154
  },
112
155
  {
113
- "subpath": "./client/conversation-submit",
114
- "summary": "Companion text submission through the application draft and canonical send action."
156
+ "subpath": "./server/codex-process",
157
+ "summary": "Advanced native host: Codex shared runtime acquisition, process leadership, verified retirement and recovery for managed execution hosts."
115
158
  },
116
159
  {
117
- "subpath": "./client/conversation-scroll",
118
- "summary": "Layout-aware scroll-follow operations."
160
+ "subpath": "./server/opencode-turn",
161
+ "summary": "Advanced native host: OpenCode authorized history projections, structured results and bounded renewal for existing hosts."
162
+ },
163
+ {
164
+ "subpath": "./server/opencode-process",
165
+ "summary": "Advanced native host: OpenCode shared runtime selection, native plugin environment, owned server lifecycle, credential-free catalogue reads and bounded API-key verification for existing hosts."
166
+ },
167
+ {
168
+ "subpath": "./shared/native-providers",
169
+ "summary": "Advanced native host: Exact native provider routes, engine configuration facts and qualified model capabilities; applications choose which models to offer."
170
+ },
171
+ {
172
+ "subpath": "./server/codex-provider",
173
+ "summary": "Advanced native host: Original Codex provider acquisition, thread selection, control, recovery, native history retirement and private renewal identity operations."
119
174
  },
120
175
  {
121
176
  "subpath": "./testing/conversation-storage",
122
- "summary": "Reusable checks for application conversation storage adapters."
177
+ "summary": "Testing: Reusable checks for application conversation storage adapters."
123
178
  },
124
179
  {
125
- "subpath": "./client/conversation-delivery",
126
- "summary": "Optimistic message delivery, immutable retry payloads, failure actions and canonical receipt reconciliation."
180
+ "subpath": "./server/native-history",
181
+ "summary": "Testing: Shared native retirement sequence retained for isolated archive and deletion-contract fixtures; native host integrations normally call their engine-specific retirement owner."
182
+ },
183
+ {
184
+ "subpath": "./testing/native-codex",
185
+ "summary": "Testing: Original Codex client, history-adapter, event and model-catalogue probes for isolated host tests; tests own endpoints, authorization and cleanup."
186
+ },
187
+ {
188
+ "subpath": "./testing/native-opencode",
189
+ "summary": "Testing: Original OpenCode loopback client for isolated host integration tests; tests own endpoints, authorization and cleanup."
127
190
  }
128
191
  ],
129
192
  "capabilities": []
@@ -4,6 +4,7 @@
4
4
  v-bind="conversation" :working="working" class="assistant-conversation__transcript"
5
5
  @load-more="adapter.actions?.loadMore?.($event)" @reload="adapter.actions?.reload?.()"
6
6
  @resend-turn="resend($event)" @cancel-turn="cancel($event)"
7
+ @check-delivery="adapter.actions?.checkDelivery?.($event)"
7
8
  @edit-turn="edit($event)" @link-click="adapter.actions?.openLink?.($event)"
8
9
  >
9
10
  <template v-for="name in transcriptSlots" #[name]="scope"><slot :name="name" v-bind="scope" /></template>
@@ -117,11 +118,33 @@ const delivery = ref(null);
117
118
  const statusId = `assistant-status-${useId()}`;
118
119
  const conversation = computed(() => {
119
120
  const saved = props.adapter.conversation;
120
- if (!props.adapter.delivery) return saved;
121
- const turns = props.adapter.delivery.turns(saved.turns || []);
122
- return { ...saved, turns, welcomeMessage: turns.length ? "" : saved.welcomeMessage };
121
+ if (!props.adapter.delivery && !saved.previewMessage && !saved.interimReply) return saved;
122
+ const { previewMessage, interimReply, ...display } = saved;
123
+ let turns = props.adapter.delivery ? props.adapter.delivery.turns(saved.turns || []) : saved.turns || [];
124
+ if (interimReply?.text && interimReply.turnId) {
125
+ const assistant = { ...interimReply, messageId: interimReply.id, role: "assistant" };
126
+ turns = turns.map(turn => turn.turnId !== interimReply.turnId ? turn : {
127
+ ...turn, assistant,
128
+ commentary: (turn.commentary || []).filter(message =>
129
+ (message.outputId || message.messageId) !== (interimReply.outputId || interimReply.id)),
130
+ ...(Array.isArray(turn.messages) ? { messages: turn.messages.filter(message =>
131
+ message.role !== "assistant" &&
132
+ (message.outputId || message.messageId) !== (interimReply.outputId || interimReply.id)).concat(assistant) } : {})
133
+ });
134
+ }
135
+ const messageId = String(previewMessage?.messageId || previewMessage?.id || "").trim();
136
+ const alreadyShown = messageId && (props.adapter.delivery?.find?.(messageId) || turns.some(turn => [
137
+ turn.system, turn.user, turn.assistant, ...(turn.thinking || []), ...(turn.commentary || []), ...(turn.messages || [])
138
+ ].some(message => String(message?.messageId || message?.id || "") === messageId)));
139
+ if (messageId && previewMessage?.text && !alreadyShown) {
140
+ const user = { messageId, role: "user", text: String(previewMessage.text) };
141
+ turns = [...turns, { turnId: messageId, user, messages: [user], optimistic: { id: messageId, status: "pending" } }];
142
+ }
143
+ // Transcription and interim replies are display only. The receipt watcher
144
+ // receives canonical turns, never these presentation or delivery overlays.
145
+ return { ...display, turns, welcomeMessage: turns.length ? "" : saved.welcomeMessage };
123
146
  });
124
- watch(() => [props.adapter.delivery, props.adapter.conversation.turns], ([controller, turns]) => {
147
+ watch([() => props.adapter.delivery, () => props.adapter.conversation.turns], ([controller, turns]) => {
125
148
  controller?.reconcile(turns || []);
126
149
  }, { immediate: true });
127
150
  const pending = computed(() => Boolean(props.adapter.delivery?.state.sending || props.adapter.composer?.pending));
@@ -0,0 +1,24 @@
1
+ <template>
2
+ <div v-if="message.delivery" role="status">
3
+ <p>{{ message.text }}</p>
4
+ <p v-if="message.delivery.error" class="text-body-small">{{ message.delivery.error }}</p>
5
+ <v-btn
6
+ variant="tonal"
7
+ min-height="48"
8
+ :disabled="message.delivery.checking"
9
+ @click="$emit('check-delivery', message.delivery.messageId)"
10
+ >
11
+ {{ message.delivery.checking ? 'Checking delivery…' : 'Check delivery' }}
12
+ </v-btn>
13
+ </div>
14
+ <slot v-else>
15
+ <LongTextPreviewBlocks compact :blocks="message.blocks" @link-click="$emit('link-click', $event)" />
16
+ </slot>
17
+ </template>
18
+
19
+ <script setup>
20
+ import LongTextPreviewBlocks from "./LongTextPreviewBlocks.vue";
21
+
22
+ defineProps({ message: { type: Object, required: true } });
23
+ defineEmits(["check-delivery", "link-click"]);
24
+ </script>
@@ -22,7 +22,7 @@ const elapsed = computed(() => {
22
22
  return minutes < 60 ? `${minutes}:${seconds}` : `${Math.floor(minutes / 60)}:${String(minutes % 60).padStart(2, "0")}:${seconds}`;
23
23
  });
24
24
  const canSet = computed(() => typeof props.state?.set === "function" && (!goal.value || goal.value.status === "complete"));
25
- const validBudget = computed(() => tokenBudget.value === "" || (Number.isSafeInteger(Number(tokenBudget.value)) && Number(tokenBudget.value) > 0));
25
+ const validBudget = computed(() => props.state?.tokenBudgetSupported === false || tokenBudget.value === "" || (Number.isSafeInteger(Number(tokenBudget.value)) && Number(tokenBudget.value) > 0));
26
26
  let timer;
27
27
  onMounted(() => watch(() => [props.state?.enabled, running.value, goal.value?.elapsedSeconds, goal.value?.sampledAt], () => {
28
28
  clearInterval(timer);
@@ -35,7 +35,7 @@ onScopeDispose(() => clearInterval(timer));
35
35
  watch(() => props.state?.enabled, enabled => { if (enabled === false) open.value = false; });
36
36
  async function setGoal() {
37
37
  if (!canSet.value || props.state.pending || !objective.value.trim() || !validBudget.value) return;
38
- const result = await props.state.set({ objective: objective.value.trim(), ...(tokenBudget.value === "" ? {} : { tokenBudget: Number(tokenBudget.value) }) });
38
+ const result = await props.state.set({ objective: objective.value.trim(), ...(props.state.tokenBudgetSupported === false || tokenBudget.value === "" ? {} : { tokenBudget: Number(tokenBudget.value) }) });
39
39
  if (result !== false && !props.state.error) { objective.value = ""; tokenBudget.value = ""; }
40
40
  }
41
41
  </script>
@@ -52,13 +52,16 @@ async function setGoal() {
52
52
  <v-card class="assistant-goal__details pa-3" max-width="360">
53
53
  <strong role="status">{{ label }}</strong>
54
54
  <p v-if="goal" class="my-2">{{ goal.objective }}</p>
55
+ <p v-if="goal?.reason" class="text-body-small my-2">{{ goal.reason }}</p>
55
56
  <p v-if="elapsed" class="text-body-small my-2">Running time: {{ elapsed }}</p>
56
57
  <v-btn v-if="running && state.pause" size="small" :disabled="state.pending" @click="state.pause()">{{ state.pending ? 'Pausing…' : 'Pause goal' }}</v-btn>
57
58
  <v-btn v-else-if="['paused', 'blocked', 'usageLimited'].includes(goal?.status) && state.resume" size="small" :disabled="state.pending" @click="state.resume()">{{ state.pending ? 'Resuming…' : 'Resume goal' }}</v-btn>
58
- <p v-if="running || goal?.status === 'paused'" class="text-body-small mt-2">Pausing prevents further automatic turns. Use Stop to interrupt the current turn.</p>
59
+ <v-btn v-if="goal && goal.status !== 'complete' && state.cancel" color="error" size="small" variant="text" :disabled="state.pending" @click="state.cancel()">Cancel goal</v-btn>
60
+ <p v-if="running || goal?.status === 'paused'" class="text-body-small mt-2">{{ state.pauseInterruptsTurn ? 'Pause stops the current turn and keeps the goal for later.' : 'Pausing prevents further automatic turns. Use Stop to interrupt the current turn.' }}</p>
61
+ <p v-if="goal && goal.status !== 'complete' && state.cancel" class="text-body-small mt-2">{{ state.cancelInterruptsTurn ? 'Cancel stops the current turn and clears the goal.' : 'Cancel removes this goal. Use Stop to interrupt a turn already running.' }}</p>
59
62
  <form v-if="canSet" class="assistant-goal__form mt-2" @submit.prevent="setGoal">
60
- <v-textarea v-model="objective" label="Goal objective" rows="2" auto-grow :disabled="state.pending" hide-details />
61
- <v-text-field v-model="tokenBudget" label="Token budget (optional)" type="number" min="1" step="1" :disabled="state.pending" :error="!validBudget" hide-details />
63
+ <v-textarea v-model="objective" label="Goal objective" rows="2" maxlength="4000" auto-grow :disabled="state.pending" hide-details />
64
+ <v-text-field v-if="state.tokenBudgetSupported !== false" v-model="tokenBudget" label="Token budget (optional)" type="number" min="1" step="1" :disabled="state.pending" :error="!validBudget" hide-details />
62
65
  <v-btn type="submit" size="small" :disabled="state.pending || !objective.trim() || !validBudget">{{ state.pending ? 'Starting…' : 'Start goal' }}</v-btn>
63
66
  </form>
64
67
  <p v-if="state.error" role="alert" class="text-error text-body-small mt-2">{{ state.error }}</p>
@@ -116,7 +116,9 @@
116
116
  <span>{{ systemLabel }}</span>
117
117
  <time v-if="entry.message.displayAt">{{ entry.message.displayAt }}</time>
118
118
  </div>
119
- <slot name="system-message" :message="entry.message"><LongTextPreviewBlocks compact :blocks="entry.message.blocks" @link-click="handleLongTextLinkClick" /></slot>
119
+ <slot name="system-message" :message="entry.message">
120
+ <AssistantConversationStatus :message="entry.message" @check-delivery="emit('check-delivery', $event)" @link-click="handleLongTextLinkClick" />
121
+ </slot>
120
122
  </div>
121
123
  </div>
122
124
 
@@ -288,6 +290,7 @@
288
290
 
289
291
  <script setup>
290
292
  import AssistantMessageAttachments from "./AssistantMessageAttachments.vue";
293
+ import AssistantConversationStatus from "./AssistantConversationStatus.vue";
291
294
  import { computed, nextTick, onBeforeUnmount, ref, watch } from "vue";
292
295
  import {
293
296
  mdiAccountOutline,
@@ -372,7 +375,7 @@ const props = defineProps({
372
375
  }
373
376
  });
374
377
 
375
- const emit = defineEmits(["cancel-turn", "edit-turn", "load-more", "link-click", "reload", "resend-turn"]);
378
+ const emit = defineEmits(["cancel-turn", "check-delivery", "edit-turn", "load-more", "link-click", "reload", "resend-turn"]);
376
379
 
377
380
  const USER_MESSAGE_COLLAPSE_MIN_CHARACTERS = 360;
378
381
  const USER_MESSAGE_PREVIEW_MAX_CHARACTERS = 280;
@@ -633,7 +636,6 @@ function messageScrollKey(message = null) {
633
636
  }
634
637
 
635
638
  const timelineScrollTrigger = computed(() => [
636
- props.visible ? "visible" : "hidden",
637
639
  props.error ? "error" : "body",
638
640
  loadingIndicatorVisible.value ? "loading" : "ready",
639
641
  displayEntries.value.length ? "has-messages" : "empty",
@@ -927,6 +929,11 @@ watch(timelineScrollTrigger, () => {
927
929
  flush: "post",
928
930
  immediate: true
929
931
  });
932
+
933
+ watch(() => props.visible, (visible) => {
934
+ clearLoadMoreScrollSnapshot();
935
+ if (visible) queueLiveBottomScroll({ force: true });
936
+ }, { flush: "post" });
930
937
  </script>
931
938
 
932
939
  <style scoped>
@@ -1,11 +1,13 @@
1
1
  export { default as AssistantConversationElement } from "./AssistantConversationElement.vue";
2
2
  export { default as AssistantTranscript } from "./AssistantTranscript.vue";
3
+ export { default as AssistantConversationStatus } from "./AssistantConversationStatus.vue";
3
4
  export { default as AssistantPromptInput } from "./AssistantPromptInput.vue";
4
5
  export { default as AssistantComposerActions } from "./AssistantComposerActions.vue";
5
6
  export { default as AssistantProgress } from "./AssistantProgress.vue";
6
7
  export { default as AssistantComposerSupport } from "./AssistantComposerSupport.vue";
7
8
  export { default as AssistantGoalControl } from "./AssistantGoalControl.vue";
8
9
  export { useAssistantSuggestions } from "./useAssistantSuggestions.js";
10
+ export { useAssistantQuestions } from "./useAssistantQuestions.js";
9
11
  export { default as LongTextPreviewBlocks } from "./LongTextPreviewBlocks.vue";
10
12
  export { default as LongTextInlineParts } from "./LongTextInlineParts.vue";
11
13
  export { useScrollToBottom } from "./useScrollToBottom.js";