wisp-agents 0.14.0 β†’ 0.15.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/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.15.0] - 2026-10-18
9
+
10
+ ### Changed
11
+
12
+ - **Calmer turn bar.** The bar under each turn now fits on one line: the read / edited / ran / searched counts open the activity log themselves (no separate **Activity** link), and **Restore to before this turn** and **Agent notes** are icon buttons next to πŸ‘ / πŸ‘Ž that stay faint until you hover the turn. After a restore, **Re-apply** shows as text so the rolled-back state stays visible.
13
+
14
+ ### Fixed
15
+
16
+ - Rating a turn with πŸ‘ / πŸ‘Ž no longer re-renders the whole card and replays its entrance animation. Every refetch re-inserted the turn nodes, which restarts their CSS animations; unchanged turns are now left in place.
17
+
8
18
  ## [0.14.0] - 2026-10-18
9
19
 
10
20
  ### Added
@@ -252,7 +262,8 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
252
262
  - Session queue with follow-ups, answers to agent questions, terminal takeover and mark-as-done.
253
263
  - Older turns are folded, with a floating collapse-all button and back-to-top.
254
264
 
255
- [Unreleased]: https://github.com/M0xoo/wisp/compare/v0.14.0...HEAD
265
+ [Unreleased]: https://github.com/M0xoo/wisp/compare/v0.15.0...HEAD
266
+ [0.15.0]: https://github.com/M0xoo/wisp/compare/v0.14.0...v0.15.0
256
267
  [0.14.0]: https://github.com/M0xoo/wisp/compare/v0.13.0...v0.14.0
257
268
  [0.13.0]: https://github.com/M0xoo/wisp/compare/v0.12.0...v0.13.0
258
269
  [0.12.0]: https://github.com/M0xoo/wisp/compare/v0.11.0...v0.12.0
package/README.md CHANGED
@@ -54,7 +54,7 @@ From there you can **follow up**, **answer its questions**, **take it over** in
54
54
  | 🀝 **Subagents** | Give the lead agent a team on other harnesses (e.g. Claude Code hands tests to Codex Γ—3). It delegates through a `delegate` tool; their edits land in its worktree. See [Subagents](#subagents). |
55
55
  | βͺ **Checkpoints & rollback** | Every turn is snapshotted first. Restore to before any turn, revert a single file, or re-run a command block from the presentation. |
56
56
  | βš–οΈ **Compare** | Run one task on two harnesses side by side, and keep the better run. |
57
- | 🧾 **Recipes & triggers** | Saved prompts with `{params}` (`wisp run release 0.14.0`), run by hand, on a schedule, when CI fails, or when an issue is labelled. |
57
+ | 🧾 **Recipes & triggers** | Saved prompts with `{params}` (`wisp run release 0.15.0`), run by hand, on a schedule, when CI fails, or when an issue is labelled. |
58
58
  | πŸ“Š **Usage** | Spend, success rate and questions per project and per harness Β· model. |
59
59
  | πŸ“± **Phone** | Questions and finished tasks on ntfy or Telegram; answer from there, or start a task with `/new`. |
60
60
  | πŸ” **Permission modes** | Choose **Plan** (read-only), **Edits** (file edits auto-accepted), or **Full auto** for each launch. `Shift+Tab` in the prompt cycles modes. |
@@ -126,7 +126,7 @@ wisp reply <id> "also do X" # follow up (answers a pending question; --mod
126
126
  wisp stop <id>
127
127
  wisp ship <id> [--draft] # commit, push and open a PR for a worktree task
128
128
  wisp merge <id> # squash-merge its PR, fast-forward your local main
129
- wisp run release 0.14.0 # run a recipe; values fill its {params} (or version=0.14.0)
129
+ wisp run release 0.15.0 # run a recipe; values fill its {params} (or version=0.15.0)
130
130
  wisp recipes # list recipes
131
131
  wisp usage [--days 7] # spend and outcomes
132
132
  wisp mcp # MCP server for other agents and editors
@@ -308,7 +308,7 @@ The launcher's **Subagents** button gives the lead agent a team: up to 6 members
308
308
 
309
309
  ## Checkpoints and rollback
310
310
 
311
- Before each turn, wisp snapshots the git working tree (pinned at `refs/wisp/checkpoints/<session>/<turn>`). **Restore to before this turn** (turn bar, session menu or ⌘K) rolls back; **Re-apply turn** undoes that. `diff` and `files` blocks have a per-file **Revert / Re-apply**, and `actions`, `terminal` and `tests` blocks run their command in the project folder with one click.
311
+ Before each turn, wisp snapshots the git working tree (pinned at `refs/wisp/checkpoints/<session>/<turn>`). **Restore to before this turn** (the βͺ icon in the turn bar, session menu or ⌘K) rolls back; **Re-apply** undoes that. `diff` and `files` blocks have a per-file **Revert / Re-apply**, and `actions`, `terminal` and `tests` blocks run their command in the project folder with one click.
312
312
 
313
313
  ## Automations
314
314
 
@@ -318,7 +318,7 @@ Before each turn, wisp snapshots the git working tree (pinned at `refs/wisp/chec
318
318
  Prepare release {version}: update CHANGELOG, README and docs/, bump package.json, tag v{version}.
319
319
  ```
320
320
 
321
- Make one from ⌘K β†’ *Recipes*, or *Save as recipe…* on any card. A recipe may pin a project, harness, model and mode. Run it from ⌘K (*Run: Release*), from the Recipes view, or from a terminal: `wisp run release 0.14.0` fills the fields in order, `wisp run release version=0.14.0` by name.
321
+ Make one from ⌘K β†’ *Recipes*, or *Save as recipe…* on any card. A recipe may pin a project, harness, model and mode. Run it from ⌘K (*Run: Release*), from the Recipes view, or from a terminal: `wisp run release 0.15.0` fills the fields in order, `wisp run release version=0.15.0` by name.
322
322
 
323
323
  **Triggers** launch sessions on their own while wisp is open (⌘K β†’ *Triggers*):
324
324
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wisp-agents",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Launch coding agents, skip the transcript, read the presentation.",
5
5
  "type": "module",
6
6
  "bin": {
package/web/css/app.css CHANGED
@@ -1080,14 +1080,23 @@ pre.code { margin: 0 0 12px; padding: 12px 14px; border-radius: 10px; background
1080
1080
 
1081
1081
  /* trail */
1082
1082
  .trail { margin-top: 18px; }
1083
- .trail-bar { display: flex; align-items: center; flex-wrap: wrap; gap: 14px; padding: 8px 2px; border-top: 1px dashed var(--line); font-size: 12px; color: var(--faint); }
1083
+ .trail-bar { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 14px; padding: 8px 2px; border-top: 1px dashed var(--line); font-size: 12px; color: var(--faint); }
1084
1084
  .tb { display: inline-flex; align-items: center; gap: 5px; font-variant-numeric: tabular-nums; }
1085
1085
  .tb .i { width: 13px; height: 13px; }
1086
1086
  .trail-bar .link { font-size: 12px; }
1087
+ /* Activity counts double as the Activity toggle, so the bar carries no separate "Activity" link. */
1088
+ .tb-acts { display: inline-flex; align-items: center; gap: 10px; margin: -3px -6px; padding: 3px 6px; border-radius: 6px; color: inherit; transition: color 0.15s, background 0.15s; }
1089
+ .tb-acts:hover, .tb-acts.on { color: var(--ink); background: var(--bg-2); }
1090
+ /* Secondary turn actions: icon-only, faint until the turn is hovered (same rule as the thumbs). */
1091
+ .tb-tools { display: inline-flex; align-items: center; gap: 1px; margin-left: -8px; }
1092
+ .tb-btn { display: inline-grid; place-items: center; width: 24px; height: 22px; border-radius: 6px; color: var(--faint); opacity: 0.5; transition: color 0.15s, background 0.15s, opacity 0.15s; }
1093
+ .tb-btn .i { width: 13px; height: 13px; }
1094
+ .tb-btn:hover, .tb-btn.on { color: var(--ink); background: var(--bg-2); }
1095
+ .turn:hover .tb-btn, .tb-btn:focus-visible, .tb-btn.on { opacity: 1; }
1087
1096
  .trail-body { padding: 8px 0 0; animation: rise 0.4s var(--ease); }
1088
1097
  .trail-body .presentation { gap: 10px; }
1089
1098
  /* Thumbs: faint until the trail is hovered or the turn is rated, so a finished turn reads the same. */
1090
- .rate { display: inline-flex; gap: 1px; margin-left: -6px; opacity: 0.5; transition: opacity 0.15s; }
1099
+ .rate { display: inline-flex; gap: 1px; opacity: 0.5; transition: opacity 0.15s; }
1091
1100
  .turn:hover .rate, .rate.rated, .rate:focus-within { opacity: 1; }
1092
1101
  .rate-btn { display: inline-grid; place-items: center; width: 24px; height: 22px; border-radius: 6px; color: var(--faint); transition: color 0.15s, background 0.15s; }
1093
1102
  .rate-btn .i { width: 13px; height: 13px; }
package/web/js/app.js CHANGED
@@ -1842,7 +1842,7 @@ function renderBody(id, { fresh }) {
1842
1842
  const dockTop = dock?.getBoundingClientRect().top;
1843
1843
  const keep = new Set();
1844
1844
  let firstNew = null;
1845
- turns.forEach((t) => {
1845
+ turns.forEach((t, i) => {
1846
1846
  const isLatest = t.id === latestId;
1847
1847
  const folded = !isLatest && !n.unfolded.has(t.id);
1848
1848
  const revKey = `${t.rolledBackAt || 0}:${(t.changes || []).map((c) => (c.reverted ? 1 : 0)).join('')}`;
@@ -1858,7 +1858,9 @@ function renderBody(id, { fresh }) {
1858
1858
  n.turnNodes.set(t.id, { key, node, status: t.status });
1859
1859
  }
1860
1860
  keep.add(t.id);
1861
- turnsEl.append(node); // re-append keeps order
1861
+ // Place the turn without re-inserting a node that's already here: re-parenting restarts its
1862
+ // CSS animations, so a refetch would replay the whole card's entrance.
1863
+ if (turnsEl.children[i] !== node) turnsEl.insertBefore(node, turnsEl.children[i] || null);
1862
1864
  });
1863
1865
  for (const [tid, v] of n.turnNodes) if (!keep.has(tid)) (v.node.remove(), n.turnNodes.delete(tid));
1864
1866
 
@@ -2084,15 +2086,18 @@ function renderTrail(s, t) {
2084
2086
  const node = el(`
2085
2087
  <div class="trail">
2086
2088
  <div class="trail-bar">
2087
- ${parts.map(([ic, l, n]) => `<span class="tb" title="${n} ${l}">${icon(ic)}${n}</span>`).join('')}
2089
+ ${acts.length ? `<button class="tb-acts" data-tab="activity" title="Show activity">${parts.length ? parts.map(([ic, l, n]) => `<span class="tb" title="${n} ${l}">${icon(ic)}${n}</span>`).join('') : 'Activity'}</button>` : ''}
2088
2090
  ${t.endedAt ? `<span class="tb">${fmtDuration(t.endedAt - t.startedAt)}</span>` : ''}
2089
2091
  ${st.costUsd ? `<span class="tb">$${st.costUsd.toFixed(2)}</span>` : ''}
2090
2092
  <span class="grow"></span>
2091
- ${canRollback ? `<button class="link xs cp-rollback ${rolledBack ? 'rolled-back' : ''}" data-a="rollback" title="${rolledBack ? 'Re-apply changes from this turn' : `Restore working tree to before this turn${t.checkpoint?.head ? ` (${t.checkpoint.head.slice(0, 7)})` : ''}`}">${icon(rolledBack ? 'rerun' : 'replay')}${rolledBack ? 'Re-apply turn' : 'Restore to before this turn'}</button>` : ''}
2092
2093
  ${t.changes?.length ? `<button class="link xs" data-tab="changes">${t.changes.length} file${t.changes.length > 1 ? 's' : ''} <span class="plus">+${totals.add}</span> <span class="minus">βˆ’${totals.del}</span></button>` : ''}
2093
- ${acts.length ? `<button class="link xs" data-tab="activity">Activity</button>` : ''}
2094
- ${t.presentation?.notes ? `<button class="link xs" data-tab="notes">Agent notes</button>` : ''}
2095
- ${feedbackOn() ? `<span class="rate ${rated ? 'rated' : ''}">${['up', 'down'].map((r) => `<button class="rate-btn ${rated === r ? 'on' : ''}" data-rate="${r}" title="${r === 'up' ? 'Good result' : 'Bad result'}: tell the wisp team" aria-pressed="${rated === r}">${icon(`thumb-${r}`)}</button>`).join('')}</span>` : ''}
2094
+ <span class="tb-tools">
2095
+ ${t.presentation?.notes ? `<button class="tb-btn" data-tab="notes" title="Agent notes" aria-label="Agent notes">${icon('text')}</button>` : ''}
2096
+ ${canRollback ? (rolledBack
2097
+ ? `<button class="link xs cp-rollback rolled-back" data-a="rollback" title="Re-apply changes from this turn">${icon('rerun')}Re-apply</button>`
2098
+ : `<button class="tb-btn cp-rollback" data-a="rollback" title="Restore working tree to before this turn${t.checkpoint?.head ? ` (${t.checkpoint.head.slice(0, 7)})` : ''}" aria-label="Restore to before this turn">${icon('replay')}</button>`) : ''}
2099
+ ${feedbackOn() ? `<span class="rate ${rated ? 'rated' : ''}">${['up', 'down'].map((r) => `<button class="rate-btn ${rated === r ? 'on' : ''}" data-rate="${r}" title="${r === 'up' ? 'Good result' : 'Bad result'}: tell the wisp team" aria-pressed="${rated === r}">${icon(`thumb-${r}`)}</button>`).join('')}</span>` : ''}
2100
+ </span>
2096
2101
  </div>
2097
2102
  <div class="trail-body" hidden></div>
2098
2103
  </div>`);