@sigrun/tui 0.11.3 → 0.11.5
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 +8 -0
- package/dist/api.d.ts +4 -2
- package/dist/api.js +4 -0
- package/dist/ui/app.js +57 -0
- package/dist/ui/conversation-pane.d.ts +7 -3
- package/dist/ui/conversation-pane.js +23 -4
- package/dist/ui/sidebar.d.ts +10 -3
- package/dist/ui/sidebar.js +17 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## [0.11.5] - 2026-11-08
|
|
2
|
+
|
|
3
|
+
- Version-locked bump with the 0.11.5 core line (no changes).
|
|
4
|
+
|
|
5
|
+
## [0.11.4] - 2026-11-08
|
|
6
|
+
|
|
7
|
+
- Version-locked bump with the 0.11.4 core line (no changes).
|
|
8
|
+
|
|
1
9
|
# Changelog
|
|
2
10
|
|
|
3
11
|
All notable changes to this project will be documented in this file.
|
package/dist/api.d.ts
CHANGED
|
@@ -21,8 +21,8 @@ import type { NoticeActionResponse, NoticesResponse } from "@sigrun/shared/notic
|
|
|
21
21
|
import type { JobsResponse, JobRunResponse } from "@sigrun/shared/views";
|
|
22
22
|
import type { JobRevisionWire, JobRunWire } from "@sigrun/shared/jobs";
|
|
23
23
|
import type { SessionMeta, SessionMetaPatch } from "@sigrun/shared/session-meta";
|
|
24
|
-
import type { AbortResponse, AmbientList, CompactResponse, ConfigureResponse, ConversationCreateResponse, ConversationForkResponse, ConversationList, ConversationViewWire, InteractionAnswered, InteractionDeclined, InteractionList, InteractionWire, MetaResponse, ModelsResponse, SettingsResponse, SettingsSectionsResponse, SubmissionResponse, WhoamiResponse } from "@sigrun/shared/views";
|
|
25
|
-
export type { AbortResponse, AmbientList, AmbientWire, CompactResponse, ConfigureResponse, ConversationCreateResponse, ConversationForkResponse, ConversationList, ConversationSummary, ConversationViewWire, InteractionAnswered, InteractionDeclined, InteractionList, InteractionWire, MetaResponse, ModelEntry, ModelsResponse, SettingsResponse, SettingsSectionsResponse, SettingsSectionEntry, SettingsSectionIndexEntry, SubmissionResponse, WhoamiResponse, } from "@sigrun/shared/views";
|
|
24
|
+
import type { AbortResponse, AmbientList, CompactResponse, ConfigureResponse, ContinueResponse, ConversationCreateResponse, ConversationForkResponse, ConversationList, ConversationViewWire, InteractionAnswered, InteractionDeclined, InteractionList, InteractionWire, MetaResponse, ModelsResponse, SettingsResponse, SettingsSectionsResponse, SubmissionResponse, WhoamiResponse } from "@sigrun/shared/views";
|
|
25
|
+
export type { AbortResponse, AmbientList, AmbientWire, CompactResponse, ConfigureResponse, ContinueResponse, ConversationCreateResponse, ConversationForkResponse, ConversationList, ConversationSummary, ConversationViewWire, InteractionAnswered, InteractionDeclined, InteractionList, InteractionWire, MetaResponse, ModelEntry, ModelsResponse, SettingsResponse, SettingsSectionsResponse, SettingsSectionEntry, SettingsSectionIndexEntry, SubmissionResponse, WhoamiResponse, } from "@sigrun/shared/views";
|
|
26
26
|
export type { CommandRunResponse, CommandsResponse, SlashCommand } from "@sigrun/shared/commands";
|
|
27
27
|
export type { JobSpec, JobSchedule, JobAction, JobWire, JobRunWire, JobRevisionWire, } from "@sigrun/shared/jobs";
|
|
28
28
|
export type { JobsResponse, JobRunResponse } from "@sigrun/shared/views";
|
|
@@ -139,6 +139,8 @@ export declare class SigrunClient {
|
|
|
139
139
|
* the summary entry arrives later as a view revision on the stream.
|
|
140
140
|
*/
|
|
141
141
|
compact(id: number, instructions?: string): Promise<CompactResponse>;
|
|
142
|
+
/** `POST /v1/conversations/:id/continue` — resume the last failed run. */
|
|
143
|
+
continueConversation(id: number): Promise<ContinueResponse>;
|
|
142
144
|
/** `GET /v1/conversations/:id/meta`. */
|
|
143
145
|
getMeta(id: number): Promise<SessionMeta>;
|
|
144
146
|
/** `PATCH /v1/conversations/:id/meta` — merges; resolves to the new meta. */
|
package/dist/api.js
CHANGED
|
@@ -159,6 +159,10 @@ export class SigrunClient {
|
|
|
159
159
|
: { instructions }),
|
|
160
160
|
});
|
|
161
161
|
}
|
|
162
|
+
/** `POST /v1/conversations/:id/continue` — resume the last failed run. */
|
|
163
|
+
continueConversation(id) {
|
|
164
|
+
return this.#send("POST", `/v1/conversations/${id}/continue`, {});
|
|
165
|
+
}
|
|
162
166
|
/** `GET /v1/conversations/:id/meta`. */
|
|
163
167
|
getMeta(id) {
|
|
164
168
|
return this.#get(`/v1/conversations/${id}/meta`);
|
package/dist/ui/app.js
CHANGED
|
@@ -166,6 +166,12 @@ export class TuiApp {
|
|
|
166
166
|
* nothing and swallow keys). Exported for the launcher's mount. */
|
|
167
167
|
mainContent;
|
|
168
168
|
#activeId;
|
|
169
|
+
/** Deleted-conversations Task 4: whether the ACTIVE conversation's
|
|
170
|
+
* RAW list row carries deleted:true. Re-checked on every sidebar
|
|
171
|
+
* refresh (sidebar.rows drops deleted rows, so the pane reads the
|
|
172
|
+
* raw listConversations() response via the sidebar). Drives the
|
|
173
|
+
* pane's dim line and the composer gate. */
|
|
174
|
+
#activeDeleted = false;
|
|
169
175
|
#unwatch;
|
|
170
176
|
/** Monotonic watch generation: a stale watch's frames must never land. */
|
|
171
177
|
#watchGen = 0;
|
|
@@ -564,6 +570,15 @@ export class TuiApp {
|
|
|
564
570
|
*/
|
|
565
571
|
async refresh() {
|
|
566
572
|
await this.sidebar.refresh();
|
|
573
|
+
// Deleted-conversations Task 4: the deleted state comes from the
|
|
574
|
+
// RAW list response (the sidebar drops deleted rows from its
|
|
575
|
+
// display rows but keeps the raw list). Re-checked on EVERY
|
|
576
|
+
// sidebar refresh, so a /delete, /restore, or another client's
|
|
577
|
+
// change lands on the next poll.
|
|
578
|
+
this.#activeDeleted =
|
|
579
|
+
this.#activeId !== undefined &&
|
|
580
|
+
this.sidebar.rawConversations.find((row) => row.id === this.#activeId)?.deleted === true;
|
|
581
|
+
this.pane.setDeleted(this.#activeDeleted);
|
|
567
582
|
// Notices Task 8: the count rides the boot refresh too (quiet —
|
|
568
583
|
// the chrome keeps its last list on a fetch failure).
|
|
569
584
|
void this.notices.refresh().catch(() => { });
|
|
@@ -595,6 +610,13 @@ export class TuiApp {
|
|
|
595
610
|
// Busy state is per-frame: unknown for the new conversation until
|
|
596
611
|
// its first frame arrives.
|
|
597
612
|
this.#busy = false;
|
|
613
|
+
// Deleted-conversations Task 4: the swap re-checks the deleted
|
|
614
|
+
// state from the raw rows the last sidebar refresh left (a
|
|
615
|
+
// refresh may be mid-flight; the next one re-checks again).
|
|
616
|
+
this.#activeDeleted =
|
|
617
|
+
this.sidebar.rawConversations.find((row) => row.id === id)
|
|
618
|
+
?.deleted === true;
|
|
619
|
+
this.pane.setDeleted(this.#activeDeleted);
|
|
598
620
|
this.sidebar.setActive(id);
|
|
599
621
|
// Notices are per-conversation context (fix wave F4): an error
|
|
600
622
|
// from the OLD conversation must never render in the new one.
|
|
@@ -832,6 +854,13 @@ export class TuiApp {
|
|
|
832
854
|
const id = this.#activeId;
|
|
833
855
|
if (id === undefined)
|
|
834
856
|
return;
|
|
857
|
+
// Deleted-conversations Task 4: a deleted conversation is
|
|
858
|
+
// read-only — the server 409s any submission; the client keeps
|
|
859
|
+
// the draft and says so instead of firing the doomed POST.
|
|
860
|
+
if (this.#activeDeleted) {
|
|
861
|
+
this.pane.showError("this conversation is deleted · /restore to restore it");
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
835
864
|
this.editor.setText("");
|
|
836
865
|
const whenBusy = this.#busy ? "steer" : undefined;
|
|
837
866
|
this.#client.submit(id, text, whenBusy).catch((error) => {
|
|
@@ -926,6 +955,20 @@ export class TuiApp {
|
|
|
926
955
|
await this.sidebar.refresh();
|
|
927
956
|
return;
|
|
928
957
|
}
|
|
958
|
+
case "delete":
|
|
959
|
+
case "restore": {
|
|
960
|
+
const id = this.#activeId;
|
|
961
|
+
if (id === undefined) {
|
|
962
|
+
this.pane.showError(`/${parsed.name} needs an active conversation`);
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
await this.#client.patchMeta(id, { deleted: parsed.name === "delete" });
|
|
966
|
+
// Re-read the RAW list row so the pane's dim line lands
|
|
967
|
+
// at once (the next poll would catch up, but the user
|
|
968
|
+
// just acted — same rule as the archive refresh above).
|
|
969
|
+
await this.refresh();
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
929
972
|
case "model":
|
|
930
973
|
case "thinking": {
|
|
931
974
|
const id = this.#activeId;
|
|
@@ -977,6 +1020,20 @@ export class TuiApp {
|
|
|
977
1020
|
this.pane.showNotice("compaction admitted — the summary arrives on the stream");
|
|
978
1021
|
return;
|
|
979
1022
|
}
|
|
1023
|
+
case "continue": {
|
|
1024
|
+
if (parsed.args !== "") {
|
|
1025
|
+
this.pane.showError("/continue takes no arguments");
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
const id = this.#activeId;
|
|
1029
|
+
if (id === undefined) {
|
|
1030
|
+
this.pane.showError("/continue needs an active conversation");
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
await this.#client.continueConversation(id);
|
|
1034
|
+
this.pane.showNotice("continuing");
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
980
1037
|
case "jobs": {
|
|
981
1038
|
// W4 Task 5: the job table into the pane's notice area — one
|
|
982
1039
|
// row per job (name, next run, last outcome), framed by a
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* wire carries no per-tool running marker, so `·` covers pending too).
|
|
9
9
|
* `pi.compaction` renders the centred `── context compacted ──` divider.
|
|
10
10
|
* A run that ended without an answer (`pi.live.runFailure`, pi-durable
|
|
11
|
-
* 1.5.0) renders one dim `run ended: <reason>[: <detail>]
|
|
12
|
-
* the last turn; the shared reader hides
|
|
13
|
-
*
|
|
11
|
+
* 1.5.0) renders one dim `run ended: <reason>[: <detail>] · /continue
|
|
12
|
+
* to resume` line under the last turn; the shared reader hides
|
|
13
|
+
* user-caused endings, `nothing_to_answer`, and superseded failures.
|
|
14
14
|
* The `[tool]`/`[thinking]` tags of
|
|
15
15
|
* the entry-era pane are gone.
|
|
16
16
|
*
|
|
@@ -134,6 +134,10 @@ export declare class ConversationPane implements Component {
|
|
|
134
134
|
surfaceStateFor(entryId: number): SurfaceState | undefined;
|
|
135
135
|
/** Append an `[error]` line (a failed composer action's surface). */
|
|
136
136
|
showError(text: string): void;
|
|
137
|
+
/** Deleted-conversations Task 4: show (or clear) the dim deleted
|
|
138
|
+
* line. The app drives it from the raw list rows, never the view
|
|
139
|
+
* stream (which carries no meta). */
|
|
140
|
+
setDeleted(deleted: boolean): void;
|
|
137
141
|
/** Append a `[notice]` line (an outcome, e.g. an abort that settled). */
|
|
138
142
|
showNotice(text: string): void;
|
|
139
143
|
/**
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* wire carries no per-tool running marker, so `·` covers pending too).
|
|
9
9
|
* `pi.compaction` renders the centred `── context compacted ──` divider.
|
|
10
10
|
* A run that ended without an answer (`pi.live.runFailure`, pi-durable
|
|
11
|
-
* 1.5.0) renders one dim `run ended: <reason>[: <detail>]
|
|
12
|
-
* the last turn; the shared reader hides
|
|
13
|
-
*
|
|
11
|
+
* 1.5.0) renders one dim `run ended: <reason>[: <detail>] · /continue
|
|
12
|
+
* to resume` line under the last turn; the shared reader hides
|
|
13
|
+
* user-caused endings, `nothing_to_answer`, and superseded failures.
|
|
14
14
|
* The `[tool]`/`[thinking]` tags of
|
|
15
15
|
* the entry-era pane are gone.
|
|
16
16
|
*
|
|
@@ -169,6 +169,10 @@ export class ConversationPane {
|
|
|
169
169
|
/** The notices' mounted components, aligned with `#notices`. */
|
|
170
170
|
#noticeComponents = [];
|
|
171
171
|
#turns = [];
|
|
172
|
+
/** Deleted-conversations Task 4: the app sets this from the RAW list
|
|
173
|
+
* row (re-checked per sidebar refresh and on conversation swap);
|
|
174
|
+
* the dim line renders under the last turn until restored. */
|
|
175
|
+
#deleted = false;
|
|
172
176
|
/** The latest view's `run ended:` line (stream state — recomputed on
|
|
173
177
|
* every `showView`, unlike the composer-owned notices). */
|
|
174
178
|
#runFailureLine;
|
|
@@ -299,7 +303,7 @@ export class ConversationPane {
|
|
|
299
303
|
this.#runFailureLine =
|
|
300
304
|
runFailure === undefined
|
|
301
305
|
? undefined
|
|
302
|
-
: `run ended: ${runFailure.reason}${runFailure.detail ? `: ${runFailure.detail}` : ""}`;
|
|
306
|
+
: `run ended: ${runFailure.reason}${runFailure.detail ? `: ${runFailure.detail}` : ""} · /continue to resume`;
|
|
303
307
|
if (this.#focus !== undefined && this.#focus >= this.#turns.length) {
|
|
304
308
|
this.#focus = undefined;
|
|
305
309
|
}
|
|
@@ -322,6 +326,15 @@ export class ConversationPane {
|
|
|
322
326
|
showError(text) {
|
|
323
327
|
this.#appendNotice({ kind: "error", text });
|
|
324
328
|
}
|
|
329
|
+
/** Deleted-conversations Task 4: show (or clear) the dim deleted
|
|
330
|
+
* line. The app drives it from the raw list rows, never the view
|
|
331
|
+
* stream (which carries no meta). */
|
|
332
|
+
setDeleted(deleted) {
|
|
333
|
+
if (deleted === this.#deleted)
|
|
334
|
+
return;
|
|
335
|
+
this.#deleted = deleted;
|
|
336
|
+
this.#rebuild();
|
|
337
|
+
}
|
|
325
338
|
/** Append a `[notice]` line (an outcome, e.g. an abort that settled). */
|
|
326
339
|
showNotice(text) {
|
|
327
340
|
this.#appendNotice({ kind: "notice", text });
|
|
@@ -433,6 +446,12 @@ export class ConversationPane {
|
|
|
433
446
|
if (this.#runFailureLine !== undefined) {
|
|
434
447
|
this.#container.addChild(new Text(this.#theme.dim(this.#runFailureLine), 0, 0));
|
|
435
448
|
}
|
|
449
|
+
// The deleted dim line (deleted-conversations Task 4) rides under
|
|
450
|
+
// the transcript, above the composer notices — verbatim copy from
|
|
451
|
+
// the plan's Global Constraints.
|
|
452
|
+
if (this.#deleted) {
|
|
453
|
+
this.#container.addChild(new Text(this.#theme.dim("this conversation is deleted · /restore to restore it"), 0, 0));
|
|
454
|
+
}
|
|
436
455
|
for (const notice of this.#notices) {
|
|
437
456
|
const component = noticeComponent(notice);
|
|
438
457
|
this.#noticeComponents.push(component);
|
package/dist/ui/sidebar.d.ts
CHANGED
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
* indent two spaces + `↳ `), names are single-line via B1's
|
|
5
5
|
* `displayName` + pi-tui's `truncateToWidth`, ages right-align via B1's
|
|
6
6
|
* `formatAge`, and the active row highlights accent-fg + bold. Archived
|
|
7
|
-
* conversations stay hidden (the tree's `includeArchived: false
|
|
8
|
-
*
|
|
7
|
+
* conversations stay hidden (the tree's `includeArchived: false`),
|
|
8
|
+
* deleted conversations stay hidden too (the tree's default deleted
|
|
9
|
+
* filter; {@link rawConversations} keeps the RAW list response so the
|
|
10
|
+
* app can still read the active conversation's deleted marker), and
|
|
11
|
+
* quiet job runs stay
|
|
9
12
|
* hidden (`includeJobRuns: false`; failed runs remain visible with their
|
|
10
13
|
* `displayName` prefix). `+ New conversation` is always the last row.
|
|
11
14
|
*
|
|
@@ -24,7 +27,7 @@
|
|
|
24
27
|
*/
|
|
25
28
|
import { type Component } from "@earendil-works/pi-tui";
|
|
26
29
|
import { type TuiTheme } from "../theme.js";
|
|
27
|
-
import type { SigrunClient } from "../api.js";
|
|
30
|
+
import type { ConversationSummary, SigrunClient } from "../api.js";
|
|
28
31
|
/** The sidebar selection: a conversation id, the `+ new` action, or an
|
|
29
32
|
* extension VIEW (UI-blocks U3 Task 5 — the app opens it in the main
|
|
30
33
|
* pane). */
|
|
@@ -64,6 +67,10 @@ export declare class Sidebar implements Component {
|
|
|
64
67
|
get rows(): readonly SidebarRow[];
|
|
65
68
|
/** The cursor's row index (0-based over {@link rows}). */
|
|
66
69
|
get cursor(): number;
|
|
70
|
+
/** The RAW list rows from the last refresh (deleted rows included).
|
|
71
|
+
* The app reads the active conversation's deleted marker here —
|
|
72
|
+
* `rows` above never carries deleted rows. */
|
|
73
|
+
get rawConversations(): readonly ConversationSummary[];
|
|
67
74
|
/**
|
|
68
75
|
* Re-fetch the listing and rebuild the rows from the tree (summary
|
|
69
76
|
* fields only — no per-row meta fetch). The cursor follows its
|
package/dist/ui/sidebar.js
CHANGED
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
* indent two spaces + `↳ `), names are single-line via B1's
|
|
5
5
|
* `displayName` + pi-tui's `truncateToWidth`, ages right-align via B1's
|
|
6
6
|
* `formatAge`, and the active row highlights accent-fg + bold. Archived
|
|
7
|
-
* conversations stay hidden (the tree's `includeArchived: false
|
|
8
|
-
*
|
|
7
|
+
* conversations stay hidden (the tree's `includeArchived: false`),
|
|
8
|
+
* deleted conversations stay hidden too (the tree's default deleted
|
|
9
|
+
* filter; {@link rawConversations} keeps the RAW list response so the
|
|
10
|
+
* app can still read the active conversation's deleted marker), and
|
|
11
|
+
* quiet job runs stay
|
|
9
12
|
* hidden (`includeJobRuns: false`; failed runs remain visible with their
|
|
10
13
|
* `displayName` prefix). `+ New conversation` is always the last row.
|
|
11
14
|
*
|
|
@@ -36,6 +39,11 @@ export class Sidebar {
|
|
|
36
39
|
#rowComponents = [];
|
|
37
40
|
/** Conversation rows only (the action row is {@link #newRow}). */
|
|
38
41
|
#rows = [];
|
|
42
|
+
/** The RAW list response from the last refresh (deleted-conversations
|
|
43
|
+
* Task 4): the app's dim-line check reads it because the display
|
|
44
|
+
* rows below DROP deleted conversations (the tree's default filter).
|
|
45
|
+
* The listing is the same one request — no extra fetch. */
|
|
46
|
+
#rawConversations = [];
|
|
39
47
|
/** The extension VIEW rows (U3 Task 5): one per declared view surface,
|
|
40
48
|
* rendered under the VIEWS group header. */
|
|
41
49
|
#views = [];
|
|
@@ -71,6 +79,12 @@ export class Sidebar {
|
|
|
71
79
|
get cursor() {
|
|
72
80
|
return this.#cursor;
|
|
73
81
|
}
|
|
82
|
+
/** The RAW list rows from the last refresh (deleted rows included).
|
|
83
|
+
* The app reads the active conversation's deleted marker here —
|
|
84
|
+
* `rows` above never carries deleted rows. */
|
|
85
|
+
get rawConversations() {
|
|
86
|
+
return this.#rawConversations;
|
|
87
|
+
}
|
|
74
88
|
/**
|
|
75
89
|
* Re-fetch the listing and rebuild the rows from the tree (summary
|
|
76
90
|
* fields only — no per-row meta fetch). The cursor follows its
|
|
@@ -78,6 +92,7 @@ export class Sidebar {
|
|
|
78
92
|
*/
|
|
79
93
|
async refresh() {
|
|
80
94
|
const list = await this.#client.listConversations();
|
|
95
|
+
this.#rawConversations = list.conversations;
|
|
81
96
|
const previousId = this.#display[this.#cursor]?.id;
|
|
82
97
|
const tree = buildConversationTree(list.conversations, {
|
|
83
98
|
includeArchived: false,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sigrun/tui",
|
|
3
|
-
"version": "0.11.
|
|
4
|
-
"description": "sigrun terminal client (pi-tui based) over the
|
|
3
|
+
"version": "0.11.5",
|
|
4
|
+
"description": "sigrun terminal client (pi-tui based) over the §5 API — spawned by the sigrun CLI, or standalone against a server URL",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"sigrun-tui": "dist/launch.js"
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@earendil-works/pi-tui": "^1.3.0",
|
|
24
|
-
"@sigrun/shared": "0.11.
|
|
24
|
+
"@sigrun/shared": "0.11.5"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"typescript": "^7.0.2",
|