@7n/tauri-components 0.13.5 → 0.13.7

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.13.7] - 2026-07-20
4
+
5
+ ### Fixed
6
+
7
+ - acp_spawn_agent тепер чекає на реальний успіх/провал initialize+session/new перед поверненням session key (усуває гонку з acp_prompt, яка проявлялась як незрозуміла 'dropped the reply channel'); request() у acp-kit.js журналить провал спавну сесії як 'failed' замість непійманого throw
8
+
9
+ ## [0.13.6] - 2026-07-20
10
+
11
+ ### Changed
12
+
13
+ - AgentDialog: перемикання агента mid-conversation тепер зберігає видимий чат-лог і передає новому агенту текстовий дайджест попередньої розмови як контекст, замість повного скидання
14
+
3
15
  ## [0.13.5] - 2026-07-20
4
16
 
5
17
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@7n/tauri-components",
3
- "version": "0.13.5",
3
+ "version": "0.13.7",
4
4
  "type": "module",
5
5
  "description": "Shared LLM agent engine + Vue/Quasar UI for Tauri apps (chat, journal, trust-tier approval).",
6
6
  "license": "MIT",
@@ -30,6 +30,7 @@
30
30
  <div v-if="turns.length" ref="logEl" class="chat-log">
31
31
  <template v-for="(turn, i) in turns" :key="i">
32
32
  <div v-if="turn.role === 'user'" class="chat-user">{{ turn.text }}</div>
33
+ <div v-else-if="turn.role === 'switch'" class="chat-switch">→ {{ turn.label }}</div>
33
34
  <template v-else>
34
35
  <div class="chat-model-label">{{ turn.agentLabel }}</div>
35
36
  <RequestView :result="turn.result" />
@@ -151,18 +152,40 @@ function apply(outcome) {
151
152
  scrollToEnd()
152
153
  }
153
154
 
155
+ /**
156
+ * Render the transcript so far as plain text, for handing off to a freshly
157
+ * spawned agent that's replacing whichever one the conversation started
158
+ * with — a new ACP session has no memory of it otherwise.
159
+ * @returns {string} context block, or '' when there's nothing to hand off
160
+ */
161
+ function handoffContext() {
162
+ const relevant = turns.value.filter(turn => turn.role !== 'switch')
163
+ if (!relevant.length) return ''
164
+ const lines = relevant.map(turn =>
165
+ turn.role === 'user'
166
+ ? `Користувач: ${turn.text}`
167
+ : `Агент (${turn.agentLabel}): ${turn.result?.summary ?? turn.result?.question ?? ''}`
168
+ )
169
+ return `Контекст попередньої розмови (продовжуєш замість іншого агента):\n${lines.join('\n')}\n\nНове повідомлення:\n`
170
+ }
171
+
154
172
  /**
155
173
  * Send the current message: start a new request, or resume the conversation
156
174
  * — unless the agent/tier dropdowns have moved away from whichever agent
157
- * spawned the active session, in which case restart as a fresh conversation
175
+ * spawned the active session, in which case restart as a fresh ACP session
158
176
  * (equivalent to closing and reopening the dialog) so the new message
159
- * actually goes to the newly selected agent.
177
+ * actually goes to the newly selected agent. The visible transcript keeps
178
+ * growing across the switch, and the prior turns are prepended as plain-text
179
+ * context to the new agent's first message so it can actually continue the
180
+ * conversation rather than starting blind.
160
181
  */
161
182
  async function send() {
162
183
  const text = prompt.value.trim()
163
184
  if (!text || running.value) return
164
- if (requestId.value && currentSpawnKey() !== activeSpawnKey.value) {
165
- turns.value = []
185
+ const switching = Boolean(requestId.value) && currentSpawnKey() !== activeSpawnKey.value
186
+ const payload = switching ? `${handoffContext()}${text}` : text
187
+ if (switching) {
188
+ turns.value.push({ role: 'switch', label: currentAgentLabel() })
166
189
  requestId.value = null
167
190
  }
168
191
  prompt.value = ''
@@ -177,7 +200,7 @@ async function send() {
177
200
  activeAgentLabel.value = currentAgentLabel()
178
201
  activeSpawnKey.value = currentSpawnKey()
179
202
  }
180
- apply(await (requestId.value ? respond(requestId.value, text) : request(text)))
203
+ apply(await (requestId.value ? respond(requestId.value, payload) : request(payload)))
181
204
  } catch (error) {
182
205
  $q.notify({ type: 'negative', message: String(error?.message ?? error) })
183
206
  } finally {
@@ -217,6 +240,14 @@ async function send() {
217
240
  margin-bottom: -4px;
218
241
  }
219
242
 
243
+ .chat-switch {
244
+ align-self: center;
245
+ font-size: 11px;
246
+ opacity: 0.55;
247
+ text-transform: uppercase;
248
+ letter-spacing: 0.02em;
249
+ }
250
+
220
251
  .chat-thinking {
221
252
  display: flex;
222
253
  align-items: center;
@@ -3,7 +3,7 @@ type: Vue Component
3
3
  title: AgentDialog.vue
4
4
  resource: npm/src/components/AgentDialog.vue
5
5
  docgen:
6
- crc: 0c46e2c2
6
+ crc: b4a8b043
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  ---
9
9
 
@@ -125,6 +125,28 @@ export function createAcpAgentKit({
125
125
  await deps.onAcpPermissionRequest(handlePermissionRequest)
126
126
  }
127
127
 
128
+ /**
129
+ * Journal a request as failed and return its structured result envelope —
130
+ * shared by a session that never spawned and a turn that errored mid-flight.
131
+ * @param {string} requestId journal record id
132
+ * @param {object[]} baseActions actions already recorded for this request
133
+ * @param {unknown} error the thrown/rejected value
134
+ * @returns {Promise<object>} structured result envelope
135
+ */
136
+ async function failedResult(requestId, baseActions, error) {
137
+ const message = String(error?.message ?? error)
138
+ await journal.update(requestId, { status: 'failed', error: message })
139
+ return {
140
+ requestId,
141
+ status: 'failed',
142
+ summary: null,
143
+ error: message,
144
+ actions: baseActions,
145
+ question: null,
146
+ pendingApproval: null
147
+ }
148
+ }
149
+
128
150
  /**
129
151
  * Run one turn, journal the result, and reset `pendingApproval`. Callers
130
152
  * must set `activeRequestId` themselves *before* starting the turn — a
@@ -141,17 +163,7 @@ export function createAcpAgentKit({
141
163
  try {
142
164
  turn = await turnPromise
143
165
  } catch (error) {
144
- const message = String(error?.message ?? error)
145
- await journal.update(requestId, { status: 'failed', error: message })
146
- return {
147
- requestId,
148
- status: 'failed',
149
- summary: null,
150
- error: message,
151
- actions: baseActions,
152
- question: null,
153
- pendingApproval: null
154
- }
166
+ return failedResult(requestId, baseActions, error)
155
167
  }
156
168
  const fields = finalizeTurn(turn)
157
169
  const actions = [...baseActions, ...turn.trace]
@@ -170,7 +182,12 @@ export function createAcpAgentKit({
170
182
  await ensureListening()
171
183
  const id = await journal.create({ intent, actor: AGENT_ACTOR })
172
184
  await journal.update(id, { status: 'running' })
173
- const session = await deps.createAcpSession(agent)
185
+ let session
186
+ try {
187
+ session = await deps.createAcpSession(agent)
188
+ } catch (error) {
189
+ return failedResult(id, [], error)
190
+ }
174
191
  activeSessionKey = session.sessionKey
175
192
  activeRequestId = id
176
193
  await journal.update(id, { acp: { agentKind: session.agentKind, sessionKey: session.sessionKey } })
@@ -3,7 +3,7 @@ type: JS Module
3
3
  title: acp-kit.js
4
4
  resource: npm/src/core/acp-kit.js
5
5
  docgen:
6
- crc: f5342002
6
+ crc: e42fda83
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  score: 100
9
9
  judgeModel: openai-codex/gpt-5.4-mini