wisp-agents 0.20.0 → 0.22.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 +35 -1
- package/README.md +17 -8
- package/bin/wisp.js +14 -6
- package/package.json +4 -3
- package/server/adapters/claude.js +12 -7
- package/server/adapters/codex.js +13 -7
- package/server/adapters/cursor.js +4 -1
- package/server/adapters/grok.js +2 -1
- package/server/adapters/index.js +7 -2
- package/server/adapters/opencode.js +5 -2
- package/server/btw.js +76 -0
- package/server/client.js +22 -2
- package/server/git.js +80 -4
- package/server/handoff.js +13 -7
- package/server/index.js +4 -0
- package/server/machines.js +15 -4
- package/server/mcp-wisp.js +2 -1
- package/server/openapi.js +28 -2
- package/server/remote.js +4 -2
- package/server/routes/machines.js +1 -1
- package/server/routes/proxy.js +6 -0
- package/server/routes/sessions.js +47 -10
- package/server/routes/settings.js +24 -38
- package/server/routes/ship.js +19 -24
- package/server/routes/uploads.js +2 -0
- package/server/runner.js +178 -12
- package/server/services/auto.js +10 -2
- package/server/services/launch.js +67 -7
- package/server/services/move.js +130 -0
- package/server/services/phone.js +20 -1
- package/server/services/places.js +150 -0
- package/server/services/ship.js +97 -10
- package/server/services/uploads.js +23 -4
- package/server/store.js +39 -6
- package/skills/wisp/SKILL.md +3 -1
- package/web/css/blocks.css +2 -0
- package/web/css/composer.css +23 -1
- package/web/css/feed.css +52 -1
- package/web/css/launcher.css +12 -0
- package/web/css/machines.css +4 -0
- package/web/css/modals.css +19 -0
- package/web/css/palette.css +6 -0
- package/web/css/ship.css +14 -0
- package/web/css/transcript.css +115 -0
- package/web/index.html +2 -0
- package/web/js/core/state.js +5 -2
- package/web/js/features/actions.js +42 -10
- package/web/js/features/btw.js +145 -0
- package/web/js/features/clones.js +7 -6
- package/web/js/features/commands.js +3 -1
- package/web/js/features/composer.js +162 -18
- package/web/js/features/feed.js +10 -10
- package/web/js/features/keys.js +2 -1
- package/web/js/features/launcher.js +121 -67
- package/web/js/features/place-menu.js +44 -0
- package/web/js/features/places.js +25 -5
- package/web/js/features/project-list.js +20 -5
- package/web/js/features/projects.js +16 -13
- package/web/js/features/settings.js +24 -3
- package/web/js/features/ship.js +66 -29
- package/web/js/features/slash.js +63 -0
- package/web/js/features/transcript.js +290 -0
- package/web/js/features/turns.js +101 -29
- package/web/js/slash-text.js +22 -0
- package/web/js/ui/kbd.js +1 -0
package/web/css/composer.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* ---------- per-item composer ---------- */
|
|
2
2
|
.composer {
|
|
3
|
-
padding: 6px 8px 8px 12px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line);
|
|
3
|
+
position: relative; padding: 6px 8px 8px 12px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line);
|
|
4
4
|
transition: box-shadow 0.15s, border-color 0.15s;
|
|
5
5
|
}
|
|
6
6
|
.composer:focus-within { background: var(--surface); border-color: color-mix(in srgb, var(--accent) 55%, var(--line-2)); box-shadow: 0 0 0 3px var(--accent-soft); }
|
|
@@ -32,6 +32,22 @@
|
|
|
32
32
|
.c-queue.on { color: var(--ink); background: var(--bg-2); }
|
|
33
33
|
.c-done:hover { color: var(--good); }
|
|
34
34
|
|
|
35
|
+
/* "/btw …" in the box: a side question. The pickers are for the next follow-up, so they step back. */
|
|
36
|
+
.composer.is-btw .c-mode, .composer.is-btw .c-runner, .composer.is-btw .c-place, .composer.is-btw .c-queue { opacity: 0.35; }
|
|
37
|
+
.c-side { font-weight: 600; color: var(--accent-ink); }
|
|
38
|
+
|
|
39
|
+
/* the / menu (features/slash.js): opens above the box, like the box's own popovers */
|
|
40
|
+
.slash-menu {
|
|
41
|
+
position: absolute; left: 8px; bottom: calc(100% + 6px); z-index: 30; min-width: 280px; max-width: calc(100% - 16px); padding: 5px;
|
|
42
|
+
border-radius: 12px; background: var(--surface); border: 1px solid var(--line); box-shadow: var(--shadow-2); animation: pop 0.18s var(--ease);
|
|
43
|
+
}
|
|
44
|
+
.slash-item { display: flex; align-items: baseline; gap: 8px; width: 100%; padding: 7px 10px; border-radius: 8px; text-align: left; font-size: 13px; color: var(--ink-2); }
|
|
45
|
+
.slash-item b { font: 600 12.5px var(--mono); color: var(--ink); }
|
|
46
|
+
.slash-args { font: 12px var(--mono); color: var(--faint); }
|
|
47
|
+
.slash-hint { margin-left: auto; padding-left: 12px; font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
48
|
+
.slash-item.on, .slash-item:hover { background: var(--bg-2); }
|
|
49
|
+
.slash-item.on b { color: var(--accent-ink); }
|
|
50
|
+
|
|
35
51
|
/* attachments tray */
|
|
36
52
|
.tray { display: flex; flex-wrap: wrap; gap: 8px; padding: 4px 0 8px; }
|
|
37
53
|
.att {
|
|
@@ -59,6 +75,12 @@
|
|
|
59
75
|
.c-runner.changed { padding-right: 8px; }
|
|
60
76
|
.c-runner.changed .rb-logo { margin-right: 4px; }
|
|
61
77
|
.c-runner:focus-visible { outline: 0; box-shadow: 0 0 0 2px var(--accent-soft), 0 0 0 1px var(--accent); }
|
|
78
|
+
/* where the next follow-up runs: a quiet icon, the computer's name once it's switched */
|
|
79
|
+
.c-place { display: inline-flex; align-items: center; gap: 5px; height: 30px; min-width: 30px; padding: 0 7px; border-radius: 8px; font-size: 12px; color: var(--muted); transition: background 0.15s, color 0.15s; }
|
|
80
|
+
.c-place .i { width: 15px; height: 15px; }
|
|
81
|
+
.c-place:hover, .c-place[aria-expanded='true'], .c-place.changed { background: var(--bg-2); color: var(--ink); }
|
|
82
|
+
.c-place.changed { padding-right: 9px; color: var(--accent); }
|
|
83
|
+
.c-place:focus-visible { outline: 0; box-shadow: 0 0 0 2px var(--accent-soft), 0 0 0 1px var(--accent); }
|
|
62
84
|
.cr-label { max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
|
|
63
85
|
|
|
64
86
|
/* ---------- task mentions (features/mentions.js) ---------- */
|
package/web/css/feed.css
CHANGED
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
.toggle-done { cursor: pointer; user-select: none; }
|
|
27
27
|
.toggle-done:hover { color: var(--ink-2); }
|
|
28
28
|
.toggle-done .chev { width: 12px; height: 12px; transition: transform 0.2s; }
|
|
29
|
-
|
|
29
|
+
/* Only the group's own heading: the cards inside have chevrons of their own. */
|
|
30
|
+
.f-group.done:not(.collapsed) > .toggle-done .chev { transform: rotate(90deg); }
|
|
30
31
|
/* Folded groups are just a row of headings: stack them tight instead of leaving a section gap each. */
|
|
31
32
|
.f-group.collapsed { margin-bottom: 2px; }
|
|
32
33
|
.f-group.collapsed h4 { margin: 0; }
|
|
@@ -78,6 +79,9 @@
|
|
|
78
79
|
font-size: 11px; font-weight: 500; padding: 0 7px; border-radius: 5px; line-height: 19px; white-space: nowrap; max-width: 120px; overflow: hidden; text-overflow: ellipsis;
|
|
79
80
|
color: color-mix(in srgb, var(--pc) 65%, var(--ink)); background: color-mix(in srgb, var(--pc) 10%, transparent);
|
|
80
81
|
}
|
|
82
|
+
/* a task in several folders: the others after its own, each in its colour */
|
|
83
|
+
.q-proj.multi { max-width: 180px; }
|
|
84
|
+
.q-also { margin-left: 4px; color: color-mix(in srgb, var(--pc) 65%, var(--ink)); }
|
|
81
85
|
.q-time { font-size: 11.5px; color: var(--faint); min-width: 26px; text-align: right; font-variant-numeric: tabular-nums; }
|
|
82
86
|
.q-queued { font-size: 11px; color: var(--accent-ink); font-weight: 600; }
|
|
83
87
|
/* one-click done on collapsed review rows; revealed on hover/focus */
|
|
@@ -181,8 +185,55 @@
|
|
|
181
185
|
.log { max-height: 340px; overflow-y: auto; margin-top: 12px; padding: 10px 12px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line); }
|
|
182
186
|
.w-actions { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
|
|
183
187
|
.w-queued { font-size: 12px; color: var(--accent-ink); font-weight: 500; }
|
|
188
|
+
/* the /btw tip (features/btw.js): until it's been used once */
|
|
189
|
+
.w-tip { display: inline-flex; align-items: center; gap: 2px; min-width: 0; animation: fade-in 0.4s 0.6s var(--ease) both; }
|
|
190
|
+
.w-tip-go { display: inline-flex; align-items: center; gap: 6px; min-width: 0; font-size: 12px; color: var(--muted); }
|
|
191
|
+
.w-tip-go:hover { color: var(--ink); }
|
|
192
|
+
.w-tip-k { color: var(--faint); }
|
|
193
|
+
.w-tip-go code { flex: none; padding: 1px 5px; border-radius: 5px; background: var(--bg-2); font: 500 11.5px var(--mono); color: var(--ink-2); }
|
|
194
|
+
.w-tip-t { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
195
|
+
.w-tip-x { flex: none; display: grid; place-items: center; width: 20px; height: 20px; border-radius: 6px; color: var(--faint); opacity: 0; transition: opacity 0.15s; }
|
|
196
|
+
.w-tip-x .i { width: 11px; height: 11px; }
|
|
197
|
+
.w-tip:hover .w-tip-x, .w-tip-x:focus-visible { opacity: 1; }
|
|
198
|
+
.w-tip-x:hover { background: var(--bg-2); color: var(--ink); }
|
|
199
|
+
@media (hover: none) { .w-tip-x { opacity: 1; } }
|
|
200
|
+
.w-actions > .peek { flex: none; white-space: nowrap; }
|
|
201
|
+
/* No room beside Show all activity and Stop on a phone: the tip gets its own line under them. */
|
|
202
|
+
@media (max-width: 720px) {
|
|
203
|
+
.w-actions:has(.w-tip) { flex-wrap: wrap; row-gap: 8px; }
|
|
204
|
+
.w-tip { order: 1; flex-basis: 100%; }
|
|
205
|
+
}
|
|
184
206
|
@keyframes tick-in { from { opacity: 0; transform: translateY(-4px); } }
|
|
185
207
|
|
|
208
|
+
/* /btw side questions (features/btw.js): plain hairline cards on the turn they were asked during */
|
|
209
|
+
.btw-list { display: grid; gap: 8px; margin-top: 14px; }
|
|
210
|
+
.btw-list:empty { display: none; }
|
|
211
|
+
.btw { position: relative; padding: 10px 40px 11px 14px; border-radius: var(--radius); background: var(--surface); border: 1px solid var(--line); }
|
|
212
|
+
.btw.new { animation: rise 0.35s var(--ease) both; }
|
|
213
|
+
.btw.landed > :is(.btw-a, .btw-err) { animation: fade-in 0.4s var(--ease) both; }
|
|
214
|
+
.btw-q { display: flex; align-items: baseline; gap: 8px; font-size: 13.5px; line-height: 20px; color: var(--ink); }
|
|
215
|
+
.btw-tag {
|
|
216
|
+
flex: none; display: inline-flex; align-items: center; gap: 4px; padding: 1px 6px 1px 4px; border-radius: 5px; cursor: default;
|
|
217
|
+
font: 600 11px/18px var(--sans); color: var(--accent-ink); background: var(--accent-soft); align-self: flex-start; margin-top: 1px;
|
|
218
|
+
}
|
|
219
|
+
.btw-tag .i { width: 12px; height: 12px; }
|
|
220
|
+
.btw-text { min-width: 0; white-space: pre-wrap; overflow-wrap: anywhere; font-weight: 500; }
|
|
221
|
+
.btw-atts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
|
|
222
|
+
.btw-a { margin-top: 6px; font-size: 13.5px; }
|
|
223
|
+
.btw-a p, .btw-a ul, .btw-a ol, .btw-a pre { margin-bottom: 8px; }
|
|
224
|
+
.btw-wait { display: flex; align-items: center; gap: 8px; margin-top: 7px; font-size: 12.5px; color: var(--muted); }
|
|
225
|
+
.btw-wait .elapsed { margin-left: auto; color: var(--faint); font-variant-numeric: tabular-nums; }
|
|
226
|
+
.btw-dots { display: inline-flex; gap: 3px; }
|
|
227
|
+
.btw-dots i { width: 5px; height: 5px; border-radius: 50%; background: var(--accent); animation: pulse 1.2s ease-in-out infinite; }
|
|
228
|
+
.btw-dots i:nth-child(2) { animation-delay: 0.2s; }
|
|
229
|
+
.btw-dots i:nth-child(3) { animation-delay: 0.4s; }
|
|
230
|
+
.btw-err { display: flex; align-items: baseline; gap: 6px; margin-top: 6px; font-size: 12.5px; color: var(--bad); }
|
|
231
|
+
.btw-err .i { flex: none; width: 13px; height: 13px; translate: 0 2px; }
|
|
232
|
+
.btw-x { position: absolute; top: 6px; right: 6px; width: 26px; height: 26px; border-radius: 7px; color: var(--faint); opacity: 0; transition: opacity 0.15s, background 0.15s, color 0.15s; }
|
|
233
|
+
.btw-x .i { width: 12px; height: 12px; }
|
|
234
|
+
.btw:hover .btw-x, .btw-x:focus-visible, .btw.st-running .btw-x { opacity: 1; }
|
|
235
|
+
@media (hover: none) { .btw-x { opacity: 1; } }
|
|
236
|
+
|
|
186
237
|
/* ask card (mid-run question) */
|
|
187
238
|
.ask-card, .b-question {
|
|
188
239
|
position: relative; padding: 16px 18px; border-radius: var(--radius); margin: 4px 0 14px;
|
package/web/css/launcher.css
CHANGED
|
@@ -41,6 +41,18 @@
|
|
|
41
41
|
.lp-chip.more:hover, .lp-chip.more.open { color: var(--ink); border-color: var(--line-2); }
|
|
42
42
|
.lp-chip.more .i { width: 12px; height: 12px; }
|
|
43
43
|
.l-projects .lp-chip[hidden] { display: none; }
|
|
44
|
+
/* one task, several folders: a small badge on a chip's corner adds it (+) or leaves it out (×) */
|
|
45
|
+
.l-projects .lp-chip { position: relative; }
|
|
46
|
+
.lp-also {
|
|
47
|
+
position: absolute; top: -6px; right: -5px; display: grid; place-items: center; width: 16px; height: 16px; border-radius: 50%;
|
|
48
|
+
background: var(--surface); border: 1px solid var(--line-2); color: var(--muted); box-shadow: var(--shadow-1);
|
|
49
|
+
opacity: 0; transform: scale(0.6); transition: opacity 0.12s, transform 0.12s, color 0.12s, background 0.12s; pointer-events: none;
|
|
50
|
+
}
|
|
51
|
+
.lp-also .i { width: 10px; height: 10px; }
|
|
52
|
+
.lp-chip:is(:hover, :focus-visible) .lp-also { opacity: 1; transform: none; pointer-events: auto; }
|
|
53
|
+
.lp-also:hover { color: #fff; background: var(--accent); border-color: var(--accent); }
|
|
54
|
+
.lp-also.x:hover { background: var(--bad); border-color: var(--bad); }
|
|
55
|
+
@media (hover: none) { .lp-also:not(.x) { opacity: 1; transform: none; pointer-events: auto; } }
|
|
44
56
|
/* narrow, with "+N" (whose list has No folder too): it goes icon-only unless picked */
|
|
45
57
|
@container launcher (max-width: 520px) { .l-projects:has(.more:not([hidden])) .lp-chip.none:not(.on) span { display: none; } }
|
|
46
58
|
/* recipes: one-click prompts under the launch bar */
|
package/web/css/machines.css
CHANGED
|
@@ -38,6 +38,10 @@
|
|
|
38
38
|
.wm-row:hover .wm-only { opacity: 1; }
|
|
39
39
|
.wm-only:hover { background: var(--bg-2); color: var(--ink); }
|
|
40
40
|
.wm-sep { height: 1px; margin: 5px 4px; background: var(--line); }
|
|
41
|
+
.wm-note { margin: 0 10px 6px; max-width: 300px; font-size: 12px; line-height: 1.4; color: var(--muted); }
|
|
42
|
+
.wm-note.warn { color: var(--warn); }
|
|
43
|
+
.place-menu .wm-check { border-radius: 50%; }
|
|
44
|
+
.place-menu .wm-row[disabled] { opacity: 0.45; cursor: default; }
|
|
41
45
|
.p-chip .p-where { width: 11px; height: 11px; color: var(--muted); }
|
|
42
46
|
.projects-modal .where { margin-bottom: 12px; }
|
|
43
47
|
.projects-modal .where .i { width: 13px; height: 13px; color: var(--muted); }
|
package/web/css/modals.css
CHANGED
|
@@ -22,6 +22,21 @@
|
|
|
22
22
|
.rr-files { display: flex; flex-wrap: wrap; gap: 6px; padding: 2px 2px 6px; }
|
|
23
23
|
.rr-file { display: inline-flex; align-items: center; gap: 5px; height: 24px; padding: 0 8px; border-radius: 7px; font-size: 12px; color: var(--ink-2); background: var(--bg-2); }
|
|
24
24
|
.rr-file .i { width: 12px; height: 12px; color: var(--muted); }
|
|
25
|
+
/* the bar never pushes Rerun out of the box: the harness name gives way first (the logo still says
|
|
26
|
+
which), then the model; on a narrow dialog the mode and place labels fold to their icons */
|
|
27
|
+
.rerun-modal { width: min(660px, 100%); }
|
|
28
|
+
.rr-box { container: rerun / inline-size; }
|
|
29
|
+
.rr-box .l-bar > .runner-btn { flex: 0 1 auto; min-width: 0; overflow: hidden; }
|
|
30
|
+
.rr-box .runner-btn :is(.rb-logo, .rb-e, .chev) { flex: none; }
|
|
31
|
+
.rr-box .runner-btn .rb-h { flex: 0 100 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
32
|
+
.rr-box .runner-btn .rb-m { flex: 0 1 auto; min-width: 0; }
|
|
33
|
+
.rr-box .where-btn span { max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
34
|
+
@container rerun (max-width: 520px) {
|
|
35
|
+
.rr-box .l-bar :is(.mode-btn, .where-btn) span, .rr-box .runner-btn .rb-h { display: none; }
|
|
36
|
+
.rr-box .runner-btn .rb-m { margin-left: 0; }
|
|
37
|
+
.rr-box .runner-btn .rb-m::before { display: none; }
|
|
38
|
+
}
|
|
39
|
+
@container rerun (max-width: 400px) { .rr-box .runner-btn :is(.rb-m, .chev) { display: none; } }
|
|
25
40
|
.overlay:not([hidden]) ~ :is(.picker, .menu) { z-index: 55; }
|
|
26
41
|
.add-row { display: flex; gap: 8px; }
|
|
27
42
|
.path-input { position: relative; flex: 1; }
|
|
@@ -47,6 +62,10 @@
|
|
|
47
62
|
.discover h4 .i { width: 14px; height: 14px; }
|
|
48
63
|
.discover h4 .muted { font-weight: 400; }
|
|
49
64
|
.prow.cloning .ppath { color: var(--muted); }
|
|
65
|
+
/* Where a project is checked out: one quiet tag per place. */
|
|
66
|
+
.ppath:has(.pplace) { display: flex; gap: 8px; font-family: inherit; }
|
|
67
|
+
.pplace { display: inline-flex; align-items: center; gap: 4px; }
|
|
68
|
+
.pplace .ic { width: 11px; height: 11px; }
|
|
50
69
|
.discover h4 .link { margin-left: auto; font-size: 12px; }
|
|
51
70
|
.prow.cloning .orb.live { margin: 0; }
|
|
52
71
|
.hint .clonedir { width: 220px; height: 24px; padding: 0 7px; margin: 0 4px; border-radius: 7px; border: 1px solid var(--line-2); background: var(--surface); color: var(--ink); font-size: 12px; outline: 0; }
|
package/web/css/palette.css
CHANGED
|
@@ -73,6 +73,12 @@
|
|
|
73
73
|
.pm-row .p-run { flex: none; }
|
|
74
74
|
.pm-row .pm-check { flex: none; width: 14px; height: 14px; color: var(--accent); visibility: hidden; }
|
|
75
75
|
.pm-row.on .pm-check { visibility: visible; }
|
|
76
|
+
/* the launcher's list: + adds a project to the task, ✓ shows it's in */
|
|
77
|
+
.pm-also { flex: none; display: grid; place-items: center; width: 20px; height: 20px; margin: -2px -4px -2px 0; border-radius: 6px; color: var(--faint); }
|
|
78
|
+
.pm-also .i { width: 13px; height: 13px; }
|
|
79
|
+
.pm-also:hover { color: var(--ink); background: var(--surface-2); }
|
|
80
|
+
.pm-row.in .pm-also { color: var(--accent); }
|
|
81
|
+
.pm-row.in .pm-name { font-weight: 500; color: var(--ink); }
|
|
76
82
|
.pm-x .i { width: 14px; height: 14px; color: var(--muted); }
|
|
77
83
|
.pm-empty { padding: 18px 10px; text-align: center; font-size: 13px; color: var(--muted); }
|
|
78
84
|
.menu .danger:hover { color: var(--bad); background: var(--del-bg); }
|
package/web/css/ship.css
CHANGED
|
@@ -42,7 +42,10 @@
|
|
|
42
42
|
.pill.st-running { --tone: var(--accent); }
|
|
43
43
|
.it-meta .m.branch, .it-meta .m:has(> .i) { display: inline-flex; align-items: center; gap: 4px; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
44
44
|
.it-meta .m.branch { font: 11px var(--mono); color: var(--ink-2); }
|
|
45
|
+
.it-meta .m.also { display: inline-flex; align-items: center; gap: 5px; color: var(--ink-2); }
|
|
45
46
|
.it-meta .m .i { width: 12px; height: 12px; flex: none; color: var(--faint); }
|
|
47
|
+
.it-meta .m.moved-to { color: var(--accent-ink); cursor: pointer; }
|
|
48
|
+
.it-meta .m.moved-to:hover { text-decoration: underline; }
|
|
46
49
|
.it-meta .m.m-plan, .it-meta .m.m-plan .i { color: var(--accent-ink); }
|
|
47
50
|
.it-meta .m.m-auto, .it-meta .m.m-auto .i { color: var(--warn); }
|
|
48
51
|
|
|
@@ -60,6 +63,14 @@
|
|
|
60
63
|
.pr-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--tone); flex: none; }
|
|
61
64
|
.pr-strip.pr-pending .pr-dot { animation: pulse 1.4s ease-in-out infinite; }
|
|
62
65
|
.pr-num { font-weight: 600; color: var(--ink); white-space: nowrap; }
|
|
66
|
+
/* a task in several repos: their PRs after the first, each with its own tone */
|
|
67
|
+
.pr-also { --tone: var(--good); display: inline-flex; align-items: center; gap: 5px; flex: none; padding: 1px 7px; border-radius: 6px; font-weight: 500; color: var(--ink); white-space: nowrap; background: color-mix(in srgb, var(--tone) 10%, transparent); }
|
|
68
|
+
.pr-also i { width: 6px; height: 6px; border-radius: 50%; background: var(--tone); }
|
|
69
|
+
.pr-also:hover { background: color-mix(in srgb, var(--tone) 18%, transparent); }
|
|
70
|
+
.pr-also.pr-pending { --tone: var(--warn); }
|
|
71
|
+
.pr-also.pr-fail { --tone: var(--bad); }
|
|
72
|
+
.pr-also.pr-merged { --tone: #8250df; }
|
|
73
|
+
.pr-also.pr-closed { --tone: var(--faint); }
|
|
63
74
|
.pr-line { color: var(--ink-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
64
75
|
.pr-strip .ghost .i { width: 13px; height: 13px; }
|
|
65
76
|
.pr-strip .pr-merge { color: var(--good); font-weight: 500; }
|
|
@@ -74,12 +85,15 @@
|
|
|
74
85
|
.ship-route code { font: 12px var(--mono); padding: 1px 6px; border-radius: 6px; background: var(--bg-2); }
|
|
75
86
|
.ship-route .i { width: 13px; height: 13px; color: var(--faint); }
|
|
76
87
|
.ship-route .muted { margin-left: auto; }
|
|
88
|
+
.ship-route .ship-repo { font-weight: 600; color: var(--ink); }
|
|
89
|
+
.ship-route + .ship-route { margin-top: -6px; }
|
|
77
90
|
.ship-field { display: flex; flex-direction: column; gap: 6px; margin: 12px 0 0; }
|
|
78
91
|
.ship-field > span { font-size: 12.5px; font-weight: 500; color: var(--ink-2); }
|
|
79
92
|
.ship-field select { width: 100%; }
|
|
80
93
|
.ship-check { display: flex; margin-top: 10px; }
|
|
81
94
|
.ship-foot { display: flex; align-items: center; gap: 12px; margin-top: 16px; }
|
|
82
95
|
.ship-foot > .muted { margin-right: auto; }
|
|
96
|
+
.ship-foot > .primary { flex: none; white-space: nowrap; }
|
|
83
97
|
.ship-foot code { font-size: 11px; }
|
|
84
98
|
.hint.bad { color: var(--bad); }
|
|
85
99
|
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/* ---------- full transcript (features/transcript.js) ----------
|
|
2
|
+
A timeline under the prompt: a hairline rail with each step's icon on it, the agent's messages as
|
|
3
|
+
prose, and the presentation after the rail ends. */
|
|
4
|
+
|
|
5
|
+
.ghost.tx-toggle.on { color: var(--accent-ink); background: var(--accent-soft); }
|
|
6
|
+
|
|
7
|
+
.tx { display: flex; flex-direction: column; }
|
|
8
|
+
.tx-head { display: flex; align-items: center; gap: 6px; min-height: 22px; margin: -6px 0 8px; font-size: 12px; color: var(--faint); white-space: nowrap; }
|
|
9
|
+
.tx-title { display: inline-flex; align-items: center; gap: 6px; font-weight: 500; color: var(--muted); }
|
|
10
|
+
.tx-count { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
11
|
+
.tx-title .i { width: 14px; height: 14px; }
|
|
12
|
+
.tx-title + .tx-count::before { content: '·'; margin-right: 6px; }
|
|
13
|
+
.tx-jump { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; }
|
|
14
|
+
.tx-jump .i { width: 12px; height: 12px; transform: rotate(90deg); }
|
|
15
|
+
|
|
16
|
+
.tx-list { position: relative; list-style: none; margin: 0 0 18px; padding: 0; }
|
|
17
|
+
.tx-list::before { content: ''; position: absolute; left: 11.5px; top: 12px; bottom: 15px; width: 1px; background: var(--line-2); }
|
|
18
|
+
/* Mid-run the rail fades out into the working panel: more is coming. */
|
|
19
|
+
.tx.live .tx-list { margin-bottom: 10px; }
|
|
20
|
+
.tx.live .tx-list::before { bottom: -10px; background: linear-gradient(var(--line-2) calc(100% - 28px), transparent); }
|
|
21
|
+
|
|
22
|
+
.tx-step, .tx-fold, .tx-end, .tx-msg { position: relative; padding-left: 28px; }
|
|
23
|
+
.tx-ic {
|
|
24
|
+
position: absolute; left: 2px; top: 5px; width: 20px; height: 20px; display: grid; place-items: center;
|
|
25
|
+
border-radius: 50%; background: var(--surface); color: var(--faint);
|
|
26
|
+
}
|
|
27
|
+
.tx-ic .i { width: 14px; height: 14px; }
|
|
28
|
+
|
|
29
|
+
/* a step: one line, opening to its command and output, its diff, or its error */
|
|
30
|
+
.tx-row {
|
|
31
|
+
display: flex; align-items: center; gap: 10px; width: 100%; min-width: 0; min-height: 30px; padding: 4px 8px;
|
|
32
|
+
border-radius: 7px; font-size: 13px; color: var(--ink-2); text-align: left; outline: 0;
|
|
33
|
+
}
|
|
34
|
+
.tx-row[role='button'] { cursor: pointer; transition: background 0.15s, color 0.15s; }
|
|
35
|
+
.tx-row[role='button']:hover, .tx-row[role='button']:focus-visible, .tx-step.open > .tx-row { background: var(--bg-2); color: var(--ink); }
|
|
36
|
+
.tx-row[role='button']:focus-visible { box-shadow: 0 0 0 2px var(--accent-soft); }
|
|
37
|
+
/* Both give way on a narrow row, the command three times faster. (Shrink factors that sum under 1
|
|
38
|
+
would leave part of the overflow in place.) */
|
|
39
|
+
.tx-label { flex: 0 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
40
|
+
.tx-cmd { flex: 0 3 auto; min-width: 0; font: 12px/1.4 var(--mono); color: var(--faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
41
|
+
.tx-row .nums { margin-left: auto; }
|
|
42
|
+
.tx-t { margin-left: auto; flex: none; font-size: 11px; color: var(--faint); font-variant-numeric: tabular-nums; opacity: 0; transition: opacity 0.15s; }
|
|
43
|
+
.tx-row .nums + .tx-t { margin-left: 0; }
|
|
44
|
+
.tx-step:hover .tx-t, .tx-msg:hover .tx-t, .tx-step.open .tx-t { opacity: 1; }
|
|
45
|
+
.tx-open {
|
|
46
|
+
flex: none; display: grid; place-items: center; width: 22px; height: 22px; margin: -2px -4px -2px 0; border-radius: 6px;
|
|
47
|
+
color: var(--faint); opacity: 0; transition: opacity 0.15s, background 0.15s, color 0.15s;
|
|
48
|
+
}
|
|
49
|
+
.tx-open .i { width: 13px; height: 13px; }
|
|
50
|
+
.tx-step:hover .tx-open, .tx-open:focus-visible { opacity: 1; }
|
|
51
|
+
.tx-open:hover { color: var(--ink); background: var(--surface); }
|
|
52
|
+
.tx-row .chev { flex: none; width: 12px; height: 12px; color: var(--faint); transition: transform 0.2s var(--ease); }
|
|
53
|
+
.tx-step.open > .tx-row .chev { transform: rotate(90deg); }
|
|
54
|
+
.tx-step.loading > .tx-row { opacity: 0.6; cursor: progress; }
|
|
55
|
+
|
|
56
|
+
.tx-step.k-error .tx-ic, .tx-step.k-error .tx-label { color: var(--bad); }
|
|
57
|
+
.tx-step:is(.k-ask, .k-wait) .tx-ic, .tx-step.k-ask .tx-label { color: var(--warn); }
|
|
58
|
+
.tx-step.k-answer .tx-ic { color: var(--good); }
|
|
59
|
+
.tx-step.by-wisp .tx-ic { color: var(--accent); }
|
|
60
|
+
.tx-step.by-wisp .tx-label { color: var(--muted); }
|
|
61
|
+
/* What you were asked and answered, and plan lines, read in full. */
|
|
62
|
+
.tx-step:is(.k-ask, .k-answer, .k-plan, .k-check, .k-background, .by-wisp) .tx-label { white-space: normal; }
|
|
63
|
+
|
|
64
|
+
.tx-more { margin: 2px 0 8px 8px; animation: rise 0.3s var(--ease); }
|
|
65
|
+
.tx-out {
|
|
66
|
+
margin: 0; padding: 10px 12px; max-height: 340px; overflow: auto; border-radius: 8px;
|
|
67
|
+
background: var(--surface-2); border: 1px solid var(--line); font: 12px/1.55 var(--mono); white-space: pre-wrap; word-break: break-word;
|
|
68
|
+
}
|
|
69
|
+
.tx-cmdline, .tx-res { display: block; }
|
|
70
|
+
.tx-cmdline { color: var(--ink); }
|
|
71
|
+
.tx-cmdline b { color: var(--faint); font-weight: 500; }
|
|
72
|
+
.tx-res { color: var(--muted); }
|
|
73
|
+
.tx-cmdline + .tx-res { margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--line-2); }
|
|
74
|
+
.tx-out.err { color: var(--bad); background: var(--del-bg); border-color: color-mix(in srgb, var(--bad) 22%, var(--line)); }
|
|
75
|
+
.tx-none { margin: 0; font-size: 12.5px; color: var(--muted); }
|
|
76
|
+
.tx-diff .presentation { gap: 8px; }
|
|
77
|
+
.tx-diff .b-diff.bare { border: 1px solid var(--line); border-radius: 8px; background: var(--surface); overflow: hidden; }
|
|
78
|
+
|
|
79
|
+
/* a message: the agent's own words, on a dot of the rail */
|
|
80
|
+
.tx-msg { margin: 8px 0 12px; padding-top: 2px; padding-right: 64px; }
|
|
81
|
+
.tx-dot { position: absolute; left: 8px; top: 9px; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 4px var(--surface); }
|
|
82
|
+
.tx-msg .prose { padding-left: 8px; font-size: 14px; color: var(--ink); }
|
|
83
|
+
.tx-msg .tx-t { position: absolute; right: 4px; top: 4px; }
|
|
84
|
+
.tx-msg:first-child { margin-top: 0; }
|
|
85
|
+
|
|
86
|
+
/* a long run of steps folds its middle */
|
|
87
|
+
.tx-fold .tx-row { color: var(--muted); cursor: pointer; transition: background 0.15s, color 0.15s; }
|
|
88
|
+
.tx-fold .tx-row:hover { background: var(--bg-2); color: var(--ink); }
|
|
89
|
+
|
|
90
|
+
.tx-end { display: flex; align-items: center; min-height: 30px; padding-left: 36px; font-size: 12.5px; font-weight: 500; color: var(--muted); }
|
|
91
|
+
.tx-end .tx-ic { color: var(--good); }
|
|
92
|
+
.tx-end.st-error .tx-ic { color: var(--bad); }
|
|
93
|
+
.tx-end.st-stopped .tx-ic { color: var(--warn); }
|
|
94
|
+
|
|
95
|
+
.tx-step.new, .tx-msg.new { animation: tick-in 0.45s var(--ease); }
|
|
96
|
+
|
|
97
|
+
/* The working panel under a live transcript: status and Stop on one line, a question under them. */
|
|
98
|
+
.w-panel.tx-panel { padding: 10px 14px; gap: 12px; }
|
|
99
|
+
.tx-panel .w-orb { padding-top: 0; align-self: flex-start; }
|
|
100
|
+
.tx-panel .w-body { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; column-gap: 12px; }
|
|
101
|
+
.tx-panel .w-status { grid-column: 1; grid-row: 1; flex-wrap: wrap; row-gap: 2px; margin: 0; }
|
|
102
|
+
.tx-panel .w-status > * { white-space: nowrap; }
|
|
103
|
+
.tx-panel .w-actions { grid-column: 2; grid-row: 1; margin: 0; }
|
|
104
|
+
.tx-panel .w-body > :not(.w-status, .w-actions) { grid-column: 1 / -1; }
|
|
105
|
+
.tx-panel .ask-slot:not(:empty) { margin-top: 10px; }
|
|
106
|
+
|
|
107
|
+
@media (max-width: 720px) {
|
|
108
|
+
/* The /btw tip doesn't fit beside the status: the row with it and Stop goes under it. */
|
|
109
|
+
.tx-panel .w-body:has(.w-tip) { display: block; }
|
|
110
|
+
.tx-panel .w-body:has(.w-tip) .w-actions { margin-top: 8px; }
|
|
111
|
+
.tx-title { display: none; }
|
|
112
|
+
.tx-cmd { display: none; }
|
|
113
|
+
.tx-msg { padding-right: 8px; }
|
|
114
|
+
.tx-msg .tx-t { display: none; }
|
|
115
|
+
}
|
package/web/index.html
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
<link rel="stylesheet" href="/css/automations.css">
|
|
23
23
|
<link rel="stylesheet" href="/css/subagents.css">
|
|
24
24
|
<link rel="stylesheet" href="/css/auto.css">
|
|
25
|
+
<link rel="stylesheet" href="/css/transcript.css">
|
|
25
26
|
</head>
|
|
26
27
|
<body>
|
|
27
28
|
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
|
|
@@ -66,6 +67,7 @@
|
|
|
66
67
|
<symbol id="i-stats" viewBox="0 0 24 24"><path d="M5 19V11M12 19V5M19 19v-6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></symbol>
|
|
67
68
|
<symbol id="i-rerun" viewBox="0 0 24 24"><path d="M19.5 12a7.5 7.5 0 11-1.8-4.9M20 4v4h-4" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></symbol>
|
|
68
69
|
<symbol id="i-replay" viewBox="0 0 24 24"><path d="M4.5 12a7.5 7.5 0 101.8-4.9M4 4v4h4" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></symbol>
|
|
70
|
+
<symbol id="i-transcript" viewBox="0 0 24 24"><path d="M6 7.5v9" stroke="currentColor" stroke-width="1.3" stroke-dasharray="1.6 1.8" stroke-linecap="round"/><circle cx="6" cy="5.5" r="1.9" fill="currentColor"/><circle cx="6" cy="12" r="1.9" fill="currentColor"/><circle cx="6" cy="18.5" r="1.9" fill="currentColor"/><path d="M11 5.5h9M11 12h6.5M11 18.5h8" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></symbol>
|
|
69
71
|
<symbol id="i-text" viewBox="0 0 24 24"><path d="M5 7h14M5 12h14M5 17h9" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></symbol>
|
|
70
72
|
<symbol id="i-back" viewBox="0 0 24 24"><path d="M15 6l-6 6 6 6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></symbol>
|
|
71
73
|
<symbol id="i-update" viewBox="0 0 24 24"><path d="M12 16V4m-5 5l5-5 5 5M5 20h14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></symbol>
|
package/web/js/core/state.js
CHANGED
|
@@ -30,15 +30,18 @@ export const S = {
|
|
|
30
30
|
drafts: new Map(),
|
|
31
31
|
replyModes: new Map(), // session id → the mode its next follow-up switches to
|
|
32
32
|
replyRunners: new Map(), // session id → { agent, model, effort } its next follow-up switches to
|
|
33
|
+
replyPlaces: new Map(), // session id → the place ('' = this computer) its next follow-up moves it to
|
|
33
34
|
};
|
|
34
35
|
export const saveOpen = () => save('wisp.open', [...S.open]);
|
|
35
36
|
export const setAgents = (list) => ((S.agents = list), save('wisp.agents', list));
|
|
36
37
|
|
|
37
38
|
// Where the launcher sends a prompt: '' = this computer, else machine ids. Several run in parallel,
|
|
38
|
-
//
|
|
39
|
+
// all in the project picked (launch.picks['']; any project runs on any place), and show up as one
|
|
40
|
+
// task. A run can work in more projects too (launch.also['']): one agent, several folders.
|
|
39
41
|
export const launch = {
|
|
40
42
|
project: load('wisp.launchProject', null), // the project picked in the first place
|
|
41
43
|
where: load('wisp.launchWhere', ['']),
|
|
42
44
|
picks: load('wisp.launchPicks', {}),
|
|
45
|
+
also: load('wisp.launchAlso', {}),
|
|
43
46
|
};
|
|
44
|
-
export const saveLaunch = () => (save('wisp.launchWhere', launch.where), save('wisp.launchPicks', launch.picks), save('wisp.launchProject', launch.project));
|
|
47
|
+
export const saveLaunch = () => (save('wisp.launchWhere', launch.where), save('wisp.launchPicks', launch.picks), save('wisp.launchAlso', launch.also), save('wisp.launchProject', launch.project));
|
|
@@ -17,7 +17,10 @@ import { collapse, expand, handOff, renderFeed, setFocus } from './feed.js';
|
|
|
17
17
|
import { launchBehind } from './launcher.js';
|
|
18
18
|
import { copyTaskId, mentionBox } from './mentions.js';
|
|
19
19
|
import { runnerPicker } from './runner-picker.js';
|
|
20
|
-
import {
|
|
20
|
+
import { connectMachine } from './machines.js';
|
|
21
|
+
import { placeMenu } from './place-menu.js';
|
|
22
|
+
import { machineById, pathAt, placeName, projectById } from './places.js';
|
|
23
|
+
import { agentById, agentName, DEFAULT_MODE, effortsFor, harnessesAt, modeMenu, MODES, paintRunnerBtn, savedEffort, savedModel } from './runners.js';
|
|
21
24
|
import { feedAnchor, keepInPlace } from './scroll.js';
|
|
22
25
|
import { mergePr, shipDialog, shipLabel, shippable } from './ship.js';
|
|
23
26
|
|
|
@@ -221,15 +224,18 @@ export async function deleteSession(id) {
|
|
|
221
224
|
});
|
|
222
225
|
}
|
|
223
226
|
|
|
224
|
-
// Launch the task again as a new session in the same
|
|
225
|
-
// optionally edited, on whichever harness, model and thinking level you pick. The
|
|
227
|
+
// Launch the task again as a new session in the same project: same prompt and attachments,
|
|
228
|
+
// optionally edited, on whichever computer, harness, model and thinking level you pick. The
|
|
229
|
+
// original stays. Another computer runs it in its own checkout (cloned there if need be).
|
|
226
230
|
export async function rerun(id) {
|
|
227
231
|
const s = S.full.get(id) || (await api(`/sessions/${id}`).catch(toastError));
|
|
228
232
|
const first = s?.turns?.[0];
|
|
229
233
|
if (!first?.prompt) return toast('Nothing to rerun — this session has no prompt.', { tone: 'bad' });
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const
|
|
234
|
+
let place = s.machine?.id || '';
|
|
235
|
+
const here = () => harnessesAt([place]).filter((x) => x.available);
|
|
236
|
+
const has = (a) => here().some((x) => x.id === a);
|
|
237
|
+
let agent = has(s.agent) ? s.agent : here()[0]?.id || s.agent;
|
|
238
|
+
const models = agentById(agent)?.models || [];
|
|
233
239
|
let model = agent === s.agent && models.some((m) => m.id === s.model) ? s.model : savedModel(agent);
|
|
234
240
|
let effort = agent === s.agent && effortsFor(agent, model).includes(s.effort) ? s.effort : savedEffort(agent, model);
|
|
235
241
|
let mode = MODES.some((m) => m.id === s.mode) ? s.mode : S.config.defaults.mode || DEFAULT_MODE.id;
|
|
@@ -242,13 +248,14 @@ export async function rerun(id) {
|
|
|
242
248
|
<textarea class="rr-prompt" rows="3" spellcheck="false"></textarea>
|
|
243
249
|
${files.length ? `<div class="rr-files">${files.map((f) => `<span class="rr-file">${icon('clip')}${esc(f.name)}</span>`).join('')}</div>` : ''}
|
|
244
250
|
<div class="l-bar">
|
|
251
|
+
<button class="tog-btn where-btn" data-a="place" aria-haspopup="menu" ${S.machines.length ? '' : 'hidden'}></button>
|
|
245
252
|
<button class="mode-btn" data-a="mode" aria-haspopup="menu"></button>
|
|
246
253
|
<button class="runner-btn" data-a="runner" title="Harness, model and thinking level" aria-haspopup="dialog"></button>
|
|
247
254
|
<span class="grow"></span>
|
|
248
255
|
<button class="primary sm" data-a="go">${icon('rerun')}Rerun</button>
|
|
249
256
|
</div>
|
|
250
257
|
</div>
|
|
251
|
-
<p class="hint"
|
|
258
|
+
<p class="hint rr-hint"></p>
|
|
252
259
|
</div>`);
|
|
253
260
|
const close = overlay(node);
|
|
254
261
|
$('.close', node).onclick = close;
|
|
@@ -264,12 +271,37 @@ export async function rerun(id) {
|
|
|
264
271
|
};
|
|
265
272
|
const runnerBtn = $('[data-a="runner"]', node);
|
|
266
273
|
const paintRunner = () => paintRunnerBtn(runnerBtn, agent, model, effort);
|
|
274
|
+
// Where it runs: where it ran, unless you pick another computer.
|
|
275
|
+
const placeBtn = $('[data-a="place"]', node);
|
|
276
|
+
const project = projectById(S.sessions.get(id)?.projectId);
|
|
277
|
+
const paintPlace = () => {
|
|
278
|
+
placeBtn.classList.toggle('on', !!place);
|
|
279
|
+
placeBtn.innerHTML = `${icon(place ? 'server' : 'laptop')}<span>${esc(placeName(place))}</span>${icon('chevron', 'chev')}`;
|
|
280
|
+
placeBtn.title = 'Run it on another computer';
|
|
281
|
+
const fresh = project && !pathAt(project, place);
|
|
282
|
+
$('.rr-hint', node).innerHTML = `Starts a new session in <code>${esc(project && !fresh ? pathAt(project, place) : s.cwd)}</code>${S.machines.length ? ` on <b>${esc(placeName(place))}</b>` : ''}${fresh ? `, in a checkout wisp clones there first` : ''}. “${esc(s.title)}” is kept as it is.`;
|
|
283
|
+
};
|
|
267
284
|
paintMode();
|
|
268
285
|
paintRunner();
|
|
286
|
+
paintPlace();
|
|
287
|
+
placeBtn.onclick = () =>
|
|
288
|
+
placeMenu(placeBtn, { title: 'Rerun on', current: place, from: s.machine?.id || '' }, (k) => {
|
|
289
|
+
place = k;
|
|
290
|
+
if (k && machineById(k)?.state === 'off') connectMachine(k);
|
|
291
|
+
// The harness has to be there too: keep it if it is, else the first one that is.
|
|
292
|
+
if (!has(agent) && here().length) {
|
|
293
|
+
agent = here()[0].id;
|
|
294
|
+
model = savedModel(agent);
|
|
295
|
+
effort = savedEffort(agent, model);
|
|
296
|
+
paintRunner();
|
|
297
|
+
}
|
|
298
|
+
paintPlace();
|
|
299
|
+
ta.focus();
|
|
300
|
+
});
|
|
269
301
|
modeBtn.onclick = () => modeMenu(modeBtn, mode, (m) => ((mode = m), paintMode(), ta.focus()));
|
|
270
302
|
// Picks here only apply to this rerun; the launcher's remembered defaults are left alone.
|
|
271
303
|
runnerBtn.onclick = () =>
|
|
272
|
-
runnerPicker(runnerBtn, { agent, model, effort, savedModel, savedEffort, auto: true }, (a, m, e) => {
|
|
304
|
+
runnerPicker(runnerBtn, { agent, model, effort, savedModel, savedEffort, places: [place], auto: true }, (a, m, e) => {
|
|
273
305
|
[agent, model, effort] = [a, m, e];
|
|
274
306
|
paintRunner();
|
|
275
307
|
});
|
|
@@ -279,8 +311,8 @@ export async function rerun(id) {
|
|
|
279
311
|
if (!prompt) return ta.focus();
|
|
280
312
|
close();
|
|
281
313
|
try {
|
|
282
|
-
const body = { prompt, cwd: s.cwd, agent, mode, model, effort, attachments: files.map((f) => f.id) };
|
|
283
|
-
const next = await launchBehind({ prompt, cwd: s.cwd, runner: { agent, model, effort } }, () => api('/sessions', { method: 'POST', body }));
|
|
314
|
+
const body = { prompt, cwd: s.cwd, also: s.also?.map((a) => a.cwd), machine: place, agent, mode, model, effort, attachments: files.map((f) => f.id) };
|
|
315
|
+
const next = await launchBehind({ prompt, cwd: s.cwd, runner: { agent, model, effort }, machine: place }, () => api('/sessions', { method: 'POST', body }));
|
|
284
316
|
setFocus(next.id);
|
|
285
317
|
} catch (err) {
|
|
286
318
|
toast(`Rerun failed: ${esc(err.message)}`, { tone: 'bad', action: { label: 'Edit again', run: () => rerun(id) } });
|