@agentprojectcontext/apx 1.28.0 → 1.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentprojectcontext/apx",
3
- "version": "1.28.0",
3
+ "version": "1.29.0",
4
4
  "description": "APX — unified CLI + daemon for the Agent Project Context (APC) standard.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -389,17 +389,28 @@
389
389
  <div class="bubble-user">${escapeHtml(m.text)}${viaIcon}</div>
390
390
  `;
391
391
  } else {
392
- t.innerHTML = `
392
+ // Consecutive agent messages (intro + post-tool answer …) read as one
393
+ // continued reply: only the FIRST shows the "Roby" header — the rest skip
394
+ // it so a tool turn isn't a stack of repeated "Roby" labels. A new header
395
+ // only appears when something (a user message) breaks the run.
396
+ const idx = messages.indexOf(m);
397
+ const prevMsg = idx > 0 ? messages[idx - 1] : null;
398
+ const agentCont = !!(prevMsg && prevMsg.role === "agent");
399
+ if (agentCont) t.classList.add("cont");
400
+ const header = agentCont ? "" : `
393
401
  <div class="role agent">
394
402
  <span class="ava sa"><img src="assets/superagent.png" alt=""/></span>
395
403
  <span class="who">${escapeHtml(agentName)}</span>
396
404
  <span class="time">${m.t || ""}</span>
397
- </div>
398
- <div class="msg-agent">${formatWordsHtml(m.text)}</div>
399
- ${m.audio ? "" /* scrubber added separately */ : ""}
405
+ </div>`;
406
+ // Copy is an inline icon at the end of the text, hover-only, so it never
407
+ // reserves an empty row. Regenerate lives in turn-actions and CSS shows it
408
+ // only on the last turn.
409
+ t.innerHTML = `
410
+ ${header}
411
+ <div class="msg-agent">${formatWordsHtml(m.text)}<button class="btn-copy" aria-label="Copiar" title="Copiar">${ICON.copy()}</button></div>
400
412
  <div class="turn-actions">
401
413
  <button class="chip btn-regen">${ICON.refresh()} Regenerar</button>
402
- <button class="chip btn-copy">${ICON.copy()} Copiar</button>
403
414
  </div>
404
415
  `;
405
416
  if (m.audio && m.dur) {
@@ -409,13 +420,13 @@
409
420
  actions.insertAdjacentHTML("beforebegin", scrubberHtml);
410
421
  wireScrubber(t, m);
411
422
  }
412
- // copy
423
+ // copy (inline icon → swaps to a check briefly)
413
424
  t.querySelector(".btn-copy")?.addEventListener("click", (e) => {
414
425
  navigator.clipboard?.writeText(m.text).catch(() => {});
415
426
  const btn = e.currentTarget;
416
427
  btn.classList.add("done");
417
- btn.innerHTML = `${ICON.check()} Copiado`;
418
- setTimeout(() => { btn.classList.remove("done"); btn.innerHTML = `${ICON.copy()} Copiar`; }, 1400);
428
+ btn.innerHTML = ICON.check();
429
+ setTimeout(() => { btn.classList.remove("done"); btn.innerHTML = ICON.copy(); }, 1400);
419
430
  });
420
431
  // regen: only the LAST agent turn can be regenerated. Past turns
421
432
  // can't because we'd have to re-issue the user prompt that came right
@@ -328,19 +328,30 @@ button { font-family: inherit; }
328
328
  .wavebar i.cur { transform: scaleY(1.25); }
329
329
  .audio .dur { font-size: 11px; color: var(--ink-3); font-variant-numeric: tabular-nums; min-width: 30px; text-align: right; }
330
330
 
331
- /* per-turn actions */
331
+ /* Continued agent messages (no repeated "Roby" header) hug the previous one. */
332
+ .turn.cont { padding-top: 0; }
333
+ .turn.cont .msg-agent { margin-top: 1px; }
334
+
335
+ /* Inline copy icon at the end of an agent message — hover-only, so it never
336
+ reserves an empty row of vertical space. */
337
+ .msg-agent .btn-copy {
338
+ display: inline-flex; align-items: center; vertical-align: -3px;
339
+ margin-left: 6px; padding: 1px; border: none; background: transparent;
340
+ cursor: pointer; color: var(--ink-3); opacity: 0; transition: opacity .15s ease, color .15s ease;
341
+ }
342
+ .turn:hover .msg-agent .btn-copy { opacity: .55; }
343
+ .msg-agent .btn-copy:hover { opacity: 1; color: var(--ink); }
344
+ .msg-agent .btn-copy.done { opacity: 1; color: oklch(0.6 0.15 150); }
345
+
346
+ /* per-turn actions — only Regenerate now, and only on the LAST agent turn.
347
+ Regenerating a past turn would replay the most-recent user prompt (not the
348
+ one that produced that reply) and silently break the flow, so it's hidden
349
+ everywhere else and takes no space. */
332
350
  .turn-actions {
333
- margin: 7px 0 0 24px; display: flex; gap: 4px;
351
+ margin: 7px 0 0 24px; display: none; gap: 4px;
334
352
  opacity: 0; transition: opacity .2s ease;
335
353
  }
336
- .turn:hover .turn-actions, .turn.last .turn-actions { opacity: 1; }
337
-
338
- /* Regenerate is only meaningful on the LAST agent turn — regenerating a
339
- past one would replay the most-recent user prompt (not the one that
340
- produced this reply) and silently break the conversation flow. Copy
341
- stays available on every turn so users can grab old replies. */
342
- .turn .btn-regen { display: none; }
343
- .turn.last .btn-regen { display: inline-flex; }
354
+ .turn.last .turn-actions { display: flex; opacity: 1; }
344
355
  .chip {
345
356
  display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
346
357
  padding: 4px 8px; border-radius: 9px; border: 1px solid var(--glass-hairline);