@aemonge-dev/pi-stardeck 0.9.0 → 0.9.2
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/docs/architecture.md +1 -1
- package/package.json +1 -1
- package/packages/pi-stardeck/README.md +4 -4
- package/packages/pi-stardeck/index.ts +10 -0
- package/packages/pi-stardeck/src/deck.ts +94 -105
- package/packages/pi-stardeck/src/gauge.ts +1 -58
- package/packages/pi-stardeck/src/title.ts +1 -0
- package/packages/pi-vim-top-border/prompt-rail.ts +0 -2
- package/packages/status/README.md +13 -24
- package/packages/status/index.ts +17 -144
- package/packages/status/src/active-work.ts +0 -9
- package/packages/status/src/ledger.ts +0 -3
- package/packages/status/src/publisher.ts +8 -173
- package/packages/status/src/reducer.ts +0 -21
- package/packages/status/src/title-activity.ts +18 -2
- package/packages/status/src/types.ts +0 -7
- package/packages/status/src/goal-block.ts +0 -377
- package/packages/status/src/goal.ts +0 -170
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { SessionSubject, SessionWorkFocus } from './active-work.ts';
|
|
2
|
-
import type { GoalHeaderState } from './goal.ts';
|
|
3
2
|
import type { ActiveRunSpanView, ActiveRuntimeRuns } from './runtime-runs.ts';
|
|
4
3
|
import type { TitleState } from './title-state.ts';
|
|
5
4
|
import type { TurnCostSnapshot } from './turn-cost.ts';
|
|
@@ -26,7 +25,6 @@ const OPEN_SPEC_WIDGET_ID = 'galactica.openspec';
|
|
|
26
25
|
const OPEN_SPEC_SCOPE_SEPARATOR_WIDGET_ID = 'galactica.openspec-scope-separator';
|
|
27
26
|
const OPEN_SPEC_PROGRESS_WIDGET_ID = 'galactica.openspec-progress';
|
|
28
27
|
const SESSION_WORK_WIDGET_ID = 'galactica.work';
|
|
29
|
-
const GOAL_WIDGET_ID = 'galactica.goal';
|
|
30
28
|
const DIAGNOSTICS_WIDGET_ID = 'galactica.diagnostics';
|
|
31
29
|
const ORCHESTRATION_WIDGET_ID = 'galactica.orchestration';
|
|
32
30
|
|
|
@@ -212,7 +210,6 @@ export function buildWidgetSnapshots(
|
|
|
212
210
|
openSpecOverview?: OpenSpecOverview;
|
|
213
211
|
openSpecProjectDetected?: boolean;
|
|
214
212
|
workFocus?: SessionWorkFocus;
|
|
215
|
-
goal?: GoalHeaderState | null;
|
|
216
213
|
} = {},
|
|
217
214
|
): FancyFooterWidget[] {
|
|
218
215
|
const compact = options.compact ?? false;
|
|
@@ -221,8 +218,7 @@ export function buildWidgetSnapshots(
|
|
|
221
218
|
|
|
222
219
|
const hasActiveFallback =
|
|
223
220
|
!options.openSpecOverview &&
|
|
224
|
-
|
|
225
|
-
Boolean(options.workFocus && options.workFocus.state !== 'clear'));
|
|
221
|
+
Boolean(options.workFocus && options.workFocus.state !== 'clear');
|
|
226
222
|
|
|
227
223
|
if (
|
|
228
224
|
options.openSpecOverview ||
|
|
@@ -269,25 +265,7 @@ export function buildWidgetSnapshots(
|
|
|
269
265
|
// Frozen contract: without an OpenSpec focus there is no progress surface —
|
|
270
266
|
// no aggregate overview, no 'no OpenSpec' narration.
|
|
271
267
|
|
|
272
|
-
if (
|
|
273
|
-
widgets.push({
|
|
274
|
-
id: GOAL_WIDGET_ID,
|
|
275
|
-
label: 'Goal',
|
|
276
|
-
description: 'Active Goal scope; its detailed objective remains in the header',
|
|
277
|
-
content: { type: 'text', text: 'Goal' },
|
|
278
|
-
icon: {
|
|
279
|
-
glyphs: { nerd: '', emoji: '🎯', unicode: '◎', ascii: 'G' },
|
|
280
|
-
color: goalHeaderColor(options.goal),
|
|
281
|
-
},
|
|
282
|
-
style: { textColor: goalHeaderColor(options.goal) },
|
|
283
|
-
layout: {
|
|
284
|
-
row: 0,
|
|
285
|
-
position: 0,
|
|
286
|
-
align: 'right',
|
|
287
|
-
fill: 'none',
|
|
288
|
-
},
|
|
289
|
-
});
|
|
290
|
-
} else if (
|
|
268
|
+
if (
|
|
291
269
|
!snapshot.openspec &&
|
|
292
270
|
!options.openSpecOverview &&
|
|
293
271
|
options.workFocus &&
|
|
@@ -321,12 +299,7 @@ export function buildWidgetSnapshots(
|
|
|
321
299
|
});
|
|
322
300
|
}
|
|
323
301
|
|
|
324
|
-
if (
|
|
325
|
-
!snapshot.openspec &&
|
|
326
|
-
!options.workFocus &&
|
|
327
|
-
!options.goal &&
|
|
328
|
-
options.openSpecFeedback
|
|
329
|
-
) {
|
|
302
|
+
if (!snapshot.openspec && !options.workFocus && options.openSpecFeedback) {
|
|
330
303
|
const unavailable = options.openSpecFeedback === 'unavailable';
|
|
331
304
|
const color: WidgetColor = unavailable ? 'warning' : 'dim';
|
|
332
305
|
const stateText =
|
|
@@ -458,6 +431,7 @@ export const HEADER_ACTIVITY_KINDS = [
|
|
|
458
431
|
'regression',
|
|
459
432
|
'verification',
|
|
460
433
|
'result-review',
|
|
434
|
+
'answering',
|
|
461
435
|
'recovery',
|
|
462
436
|
'awaiting-validation',
|
|
463
437
|
'awaiting-input',
|
|
@@ -494,26 +468,14 @@ export type HeaderLifecycle = (typeof HEADER_LIFECYCLES)[number];
|
|
|
494
468
|
|
|
495
469
|
export const HEADER_SELECTION_SOURCES = [
|
|
496
470
|
'openspec',
|
|
497
|
-
'goal',
|
|
498
471
|
'session-work',
|
|
499
472
|
'subject',
|
|
500
473
|
] as const;
|
|
501
474
|
|
|
502
|
-
export type HeaderSelectionGoalDetail = {
|
|
503
|
-
status: string;
|
|
504
|
-
elapsedSeconds?: number;
|
|
505
|
-
tasksDone?: number;
|
|
506
|
-
tasksActive?: number;
|
|
507
|
-
tasksTotal?: number;
|
|
508
|
-
tokensUsed?: number;
|
|
509
|
-
tokenBudget?: number;
|
|
510
|
-
};
|
|
511
|
-
|
|
512
475
|
export type HeaderSelection = {
|
|
513
476
|
source: (typeof HEADER_SELECTION_SOURCES)[number];
|
|
514
477
|
titles: string[];
|
|
515
478
|
color: WidgetColor;
|
|
516
|
-
goal?: HeaderSelectionGoalDetail;
|
|
517
479
|
};
|
|
518
480
|
|
|
519
481
|
export const HEADER_SUGGESTIONS = [
|
|
@@ -548,7 +510,6 @@ export type HeaderStatusEvent = {
|
|
|
548
510
|
backgroundActivity: boolean;
|
|
549
511
|
approvalRequired: boolean;
|
|
550
512
|
blocked: boolean;
|
|
551
|
-
openGoals?: number;
|
|
552
513
|
activeRunSpans?: ActiveRunSpanView[];
|
|
553
514
|
/** Versioned deck-title-state contract for the deck's upper half. */
|
|
554
515
|
title?: TitleState;
|
|
@@ -586,7 +547,6 @@ function selectedOpenSpecTitles(
|
|
|
586
547
|
export function headerSelection(options: {
|
|
587
548
|
openSpec?: OpenSpecState | null;
|
|
588
549
|
focusedTaskId?: string;
|
|
589
|
-
goal?: GoalHeaderState | null;
|
|
590
550
|
workFocus?: SessionWorkFocus;
|
|
591
551
|
subject?: SessionSubject;
|
|
592
552
|
}): HeaderSelection | null {
|
|
@@ -597,26 +557,6 @@ export function headerSelection(options: {
|
|
|
597
557
|
color: openSpecColor(options.openSpec),
|
|
598
558
|
};
|
|
599
559
|
}
|
|
600
|
-
if (options.goal) {
|
|
601
|
-
const goal = options.goal;
|
|
602
|
-
const detail = {
|
|
603
|
-
status: goal.status,
|
|
604
|
-
...(goal.elapsedSeconds !== undefined
|
|
605
|
-
? { elapsedSeconds: goal.elapsedSeconds }
|
|
606
|
-
: {}),
|
|
607
|
-
...(goal.tasksDone !== undefined ? { tasksDone: goal.tasksDone } : {}),
|
|
608
|
-
...(goal.tasksActive !== undefined ? { tasksActive: goal.tasksActive } : {}),
|
|
609
|
-
...(goal.tasksTotal !== undefined ? { tasksTotal: goal.tasksTotal } : {}),
|
|
610
|
-
...(goal.tokensUsed !== undefined ? { tokensUsed: goal.tokensUsed } : {}),
|
|
611
|
-
...(goal.tokenBudget !== undefined ? { tokenBudget: goal.tokenBudget } : {}),
|
|
612
|
-
};
|
|
613
|
-
return {
|
|
614
|
-
source: 'goal',
|
|
615
|
-
titles: [goal.objective ? normalizeInline(goal.objective) : 'Goal'],
|
|
616
|
-
color: goalHeaderColor(goal),
|
|
617
|
-
goal: detail,
|
|
618
|
-
};
|
|
619
|
-
}
|
|
620
560
|
if (options.workFocus && options.workFocus.state !== 'clear') {
|
|
621
561
|
return {
|
|
622
562
|
source: 'session-work',
|
|
@@ -713,86 +653,6 @@ export function headerLifecycleForActivity(
|
|
|
713
653
|
return 'working';
|
|
714
654
|
}
|
|
715
655
|
|
|
716
|
-
export function goalHeaderLifecycle(
|
|
717
|
-
goal: GoalHeaderState,
|
|
718
|
-
activeFallback: HeaderLifecycle = 'working',
|
|
719
|
-
): HeaderLifecycle {
|
|
720
|
-
if (goal.status === 'blocked') return 'blocked';
|
|
721
|
-
if (goal.status === 'active' && !goal.waiting) return activeFallback;
|
|
722
|
-
return 'waiting';
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
export function goalHeaderActivityPath(
|
|
726
|
-
goal: GoalHeaderState,
|
|
727
|
-
automaticTurnLimit?: number,
|
|
728
|
-
): HeaderActivitySegment[] {
|
|
729
|
-
if (goal.status === 'complete') {
|
|
730
|
-
return [
|
|
731
|
-
{
|
|
732
|
-
id: 'awaiting-human-validation',
|
|
733
|
-
label: 'awaiting Human validation',
|
|
734
|
-
compact: 'validation',
|
|
735
|
-
},
|
|
736
|
-
];
|
|
737
|
-
}
|
|
738
|
-
if (goal.status === 'blocked') {
|
|
739
|
-
return [{ id: 'goal-blocked', label: 'Goal blocked', compact: 'blocked' }];
|
|
740
|
-
}
|
|
741
|
-
if (goal.status === 'paused') {
|
|
742
|
-
return [{ id: 'goal-paused', label: 'Goal paused', compact: 'paused' }];
|
|
743
|
-
}
|
|
744
|
-
if (goal.status === 'usage_limited') {
|
|
745
|
-
return [
|
|
746
|
-
{
|
|
747
|
-
id: 'goal-usage-limited',
|
|
748
|
-
label: 'Goal usage limited',
|
|
749
|
-
compact: 'usage limit',
|
|
750
|
-
},
|
|
751
|
-
];
|
|
752
|
-
}
|
|
753
|
-
if (goal.status === 'budget_limited') {
|
|
754
|
-
return [
|
|
755
|
-
{
|
|
756
|
-
id: 'goal-budget-limited',
|
|
757
|
-
label: 'Goal budget limited',
|
|
758
|
-
compact: 'budget limit',
|
|
759
|
-
},
|
|
760
|
-
];
|
|
761
|
-
}
|
|
762
|
-
if (goal.status === 'queued') {
|
|
763
|
-
return [{ id: 'goal-queued', label: 'Goal queued', compact: 'queued' }];
|
|
764
|
-
}
|
|
765
|
-
if (goal.waiting) {
|
|
766
|
-
return [
|
|
767
|
-
{
|
|
768
|
-
id: 'goal-waiting',
|
|
769
|
-
label: 'waiting for external event',
|
|
770
|
-
compact: 'waiting',
|
|
771
|
-
},
|
|
772
|
-
];
|
|
773
|
-
}
|
|
774
|
-
return [
|
|
775
|
-
{
|
|
776
|
-
id: 'goal-work',
|
|
777
|
-
label: 'completing Goal',
|
|
778
|
-
compact: 'Goal',
|
|
779
|
-
...(automaticTurnLimit === undefined
|
|
780
|
-
? {}
|
|
781
|
-
: {
|
|
782
|
-
current: Math.min(goal.automaticModelTurns, automaticTurnLimit),
|
|
783
|
-
total: automaticTurnLimit,
|
|
784
|
-
}),
|
|
785
|
-
},
|
|
786
|
-
];
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
function goalHeaderColor(goal: GoalHeaderState): WidgetColor {
|
|
790
|
-
if (goal.status === 'blocked') return 'error';
|
|
791
|
-
if (goal.status === 'active' && !goal.waiting) return 'accent';
|
|
792
|
-
if (goal.status === 'complete') return 'warning';
|
|
793
|
-
return 'dim';
|
|
794
|
-
}
|
|
795
|
-
|
|
796
656
|
export function orchestrationHeaderLifecycle(
|
|
797
657
|
state: OrchestrationState | null | undefined,
|
|
798
658
|
): HeaderLifecycle | undefined {
|
|
@@ -1032,9 +892,6 @@ export function buildHeaderStatusEvent(
|
|
|
1032
892
|
activityPath?: HeaderActivitySegment[];
|
|
1033
893
|
taskflowPhase?: string;
|
|
1034
894
|
fallbackTitles?: string[];
|
|
1035
|
-
goal?: GoalHeaderState | null;
|
|
1036
|
-
openGoals?: number;
|
|
1037
|
-
goalAutomaticTurnLimit?: number;
|
|
1038
895
|
activeRuns?: ActiveRuntimeRuns;
|
|
1039
896
|
activeRunSpans?: ActiveRunSpanView[];
|
|
1040
897
|
idleMs?: number;
|
|
@@ -1060,7 +917,6 @@ export function buildHeaderStatusEvent(
|
|
|
1060
917
|
const orchestration = options.orchestration;
|
|
1061
918
|
const blocked =
|
|
1062
919
|
orchestration?.state === 'blocked' ||
|
|
1063
|
-
options.goal?.status === 'blocked' ||
|
|
1064
920
|
Boolean(
|
|
1065
921
|
options.openSpec &&
|
|
1066
922
|
['blocked', 'error', 'failed'].includes(options.openSpec.phase),
|
|
@@ -1071,33 +927,22 @@ export function buildHeaderStatusEvent(
|
|
|
1071
927
|
const taskflowPhase = boundedTaskflowPhase(options.taskflowPhase);
|
|
1072
928
|
const activityPath =
|
|
1073
929
|
options.activityPath ??
|
|
1074
|
-
(taskflowPhase ? taskflowPhaseHeaderActivityPath(taskflowPhase) : undefined)
|
|
1075
|
-
(options.goal
|
|
1076
|
-
? goalHeaderActivityPath(options.goal, options.goalAutomaticTurnLimit)
|
|
1077
|
-
: undefined);
|
|
930
|
+
(taskflowPhase ? taskflowPhaseHeaderActivityPath(taskflowPhase) : undefined);
|
|
1078
931
|
const ordinaryLifecycle =
|
|
1079
932
|
orchestrationLifecycle ??
|
|
1080
933
|
options.lifecycle ??
|
|
1081
934
|
headerLifecycleForActivity(activity) ??
|
|
1082
|
-
(options.
|
|
1083
|
-
? 'working'
|
|
1084
|
-
: options.workFocus?.state === 'validation'
|
|
1085
|
-
? 'waiting'
|
|
1086
|
-
: 'listening');
|
|
935
|
+
(options.workFocus?.state === 'validation' ? 'waiting' : 'listening');
|
|
1087
936
|
const lifecycle: HeaderLifecycle = blocked
|
|
1088
937
|
? 'blocked'
|
|
1089
938
|
: options.lifecycle === 'aborted'
|
|
1090
939
|
? 'aborted'
|
|
1091
940
|
: ((taskflowPhase ? taskflowPhaseHeaderLifecycle(taskflowPhase) : undefined) ??
|
|
1092
|
-
(options.goal
|
|
1093
|
-
? goalHeaderLifecycle(options.goal, ordinaryLifecycle)
|
|
1094
|
-
: undefined) ??
|
|
1095
941
|
ordinaryLifecycle);
|
|
1096
942
|
const projectedActivity =
|
|
1097
943
|
activityPath !== undefined ? { activityPath } : activity ? { activity } : {};
|
|
1098
944
|
const selection = headerSelection(options);
|
|
1099
|
-
const approvalRequired =
|
|
1100
|
-
options.workFocus?.state === 'validation' || options.goal?.status === 'complete';
|
|
945
|
+
const approvalRequired = options.workFocus?.state === 'validation';
|
|
1101
946
|
const suggestion = headerSuggestion({
|
|
1102
947
|
lifecycle,
|
|
1103
948
|
activity,
|
|
@@ -1115,13 +960,6 @@ export function buildHeaderStatusEvent(
|
|
|
1115
960
|
color: openSpecColor(state),
|
|
1116
961
|
...projectedActivity,
|
|
1117
962
|
};
|
|
1118
|
-
} else if (options.goal) {
|
|
1119
|
-
work = {
|
|
1120
|
-
lifecycle,
|
|
1121
|
-
titles: ['Goal'],
|
|
1122
|
-
color: goalHeaderColor(options.goal),
|
|
1123
|
-
...projectedActivity,
|
|
1124
|
-
};
|
|
1125
963
|
} else if (options.workFocus && options.workFocus.state !== 'clear') {
|
|
1126
964
|
work = {
|
|
1127
965
|
lifecycle,
|
|
@@ -1162,7 +1000,6 @@ export function buildHeaderStatusEvent(
|
|
|
1162
1000
|
|
|
1163
1001
|
return {
|
|
1164
1002
|
protocol: 1,
|
|
1165
|
-
...(options.openGoals ? { openGoals: options.openGoals } : {}),
|
|
1166
1003
|
...(options.activeRunSpans ? { activeRunSpans: options.activeRunSpans } : {}),
|
|
1167
1004
|
...(options.idleMs ? { idleMs: options.idleMs } : {}),
|
|
1168
1005
|
...(options.title ? { title: options.title } : {}),
|
|
@@ -1188,9 +1025,7 @@ export function buildHeaderStatusEvent(
|
|
|
1188
1025
|
}
|
|
1189
1026
|
: null,
|
|
1190
1027
|
backgroundActivity: Boolean(
|
|
1191
|
-
orchestration?.state === 'running' ||
|
|
1192
|
-
(orchestration?.activeWorkers ?? 0) > 0 ||
|
|
1193
|
-
(options.goal?.status === 'active' && !options.goal.waiting),
|
|
1028
|
+
orchestration?.state === 'running' || (orchestration?.activeWorkers ?? 0) > 0,
|
|
1194
1029
|
),
|
|
1195
1030
|
approvalRequired,
|
|
1196
1031
|
blocked,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { LedgerEvent } from './ledger.ts';
|
|
2
|
-
import type { GoalHeaderState } from './goal.ts';
|
|
3
2
|
import type { OpenSpecFocus, SessionSubject, SessionWorkFocus } from './active-work.ts';
|
|
4
3
|
import type {
|
|
5
4
|
CockpitSnapshot,
|
|
@@ -20,7 +19,6 @@ import type {
|
|
|
20
19
|
|
|
21
20
|
export type ReducerFacts = {
|
|
22
21
|
openSpec: OpenSpecFocus;
|
|
23
|
-
goal: GoalHeaderState | null;
|
|
24
22
|
work: SessionWorkFocus;
|
|
25
23
|
subject: SessionSubject;
|
|
26
24
|
runs: ReadonlyMap<string, RunSpan>;
|
|
@@ -32,7 +30,6 @@ export const NO_SELECTION: SelectionState = { kind: 'none' };
|
|
|
32
30
|
export function emptyFacts(): ReducerFacts {
|
|
33
31
|
return {
|
|
34
32
|
openSpec: { mode: 'none' },
|
|
35
|
-
goal: null,
|
|
36
33
|
work: { state: 'clear' },
|
|
37
34
|
subject: { state: 'clear' },
|
|
38
35
|
runs: new Map(),
|
|
@@ -43,7 +40,6 @@ export function emptyFacts(): ReducerFacts {
|
|
|
43
40
|
/** The single selection resolver. Priority order is contract-frozen. */
|
|
44
41
|
export function resolveSelection(facts: {
|
|
45
42
|
openSpec: OpenSpecFocus;
|
|
46
|
-
goal: GoalHeaderState | null;
|
|
47
43
|
work: SessionWorkFocus;
|
|
48
44
|
subject: SessionSubject;
|
|
49
45
|
}): SelectionState {
|
|
@@ -54,13 +50,6 @@ export function resolveSelection(facts: {
|
|
|
54
50
|
task: facts.openSpec.taskId,
|
|
55
51
|
};
|
|
56
52
|
}
|
|
57
|
-
if (facts.goal) {
|
|
58
|
-
return {
|
|
59
|
-
kind: 'goal',
|
|
60
|
-
status: facts.goal.status,
|
|
61
|
-
waiting: facts.goal.waiting,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
53
|
if (facts.work.state !== 'clear') {
|
|
65
54
|
return {
|
|
66
55
|
kind: 'session-work',
|
|
@@ -84,8 +73,6 @@ export function applyEvent(facts: ReducerFacts, event: LedgerEvent): ReducerFact
|
|
|
84
73
|
switch (event.type) {
|
|
85
74
|
case 'focus/openspec':
|
|
86
75
|
return { ...facts, openSpec: event.focus };
|
|
87
|
-
case 'focus/goal':
|
|
88
|
-
return { ...facts, goal: event.goal };
|
|
89
76
|
case 'focus/work':
|
|
90
77
|
return { ...facts, work: event.work };
|
|
91
78
|
case 'focus/subject':
|
|
@@ -166,14 +153,6 @@ export function deriveTitle(snapshot: CockpitSnapshot): string {
|
|
|
166
153
|
case 'openspec-task':
|
|
167
154
|
detail = `${selection.change} › ${selection.task}`;
|
|
168
155
|
break;
|
|
169
|
-
case 'goal':
|
|
170
|
-
detail =
|
|
171
|
-
selection.status === 'complete'
|
|
172
|
-
? 'Validate › Goal'
|
|
173
|
-
: selection.waiting
|
|
174
|
-
? 'Goal › waiting'
|
|
175
|
-
: 'Goal';
|
|
176
|
-
break;
|
|
177
156
|
case 'session-work':
|
|
178
157
|
detail =
|
|
179
158
|
selection.phase === 'validation'
|
|
@@ -138,6 +138,7 @@ function sameActivity(
|
|
|
138
138
|
|
|
139
139
|
export class TitleActivityFeed {
|
|
140
140
|
private newest: TitleActivityState | null = null;
|
|
141
|
+
private ownerCallId: string | null = null;
|
|
141
142
|
|
|
142
143
|
/** The newest observed event, or null when idle. */
|
|
143
144
|
current(): TitleActivityState | null {
|
|
@@ -148,6 +149,17 @@ export class TitleActivityFeed {
|
|
|
148
149
|
reset(): boolean {
|
|
149
150
|
if (this.newest === null) return false;
|
|
150
151
|
this.newest = null;
|
|
152
|
+
this.ownerCallId = null;
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Observe a tool-execution end: the current activity clears when this
|
|
157
|
+
* call authored it — a finished tool never lingers through the gaps
|
|
158
|
+
* that follow (sticky-label fix, quiet-activity-row amendment). */
|
|
159
|
+
finish(toolCallId: string): boolean {
|
|
160
|
+
if (this.newest === null || this.ownerCallId !== toolCallId) return false;
|
|
161
|
+
this.newest = null;
|
|
162
|
+
this.ownerCallId = null;
|
|
151
163
|
return true;
|
|
152
164
|
}
|
|
153
165
|
|
|
@@ -180,7 +192,9 @@ export class TitleActivityFeed {
|
|
|
180
192
|
span: turnSpanId ?? toolCallId,
|
|
181
193
|
parent: null,
|
|
182
194
|
};
|
|
183
|
-
|
|
195
|
+
const changed = this.set(event);
|
|
196
|
+
if (changed) this.ownerCallId = toolCallId;
|
|
197
|
+
return changed;
|
|
184
198
|
}
|
|
185
199
|
|
|
186
200
|
/**
|
|
@@ -204,12 +218,14 @@ export class TitleActivityFeed {
|
|
|
204
218
|
shortTarget(result.agent) ??
|
|
205
219
|
shortTarget(callsAgentName(args, index)) ??
|
|
206
220
|
FALLBACK_SUBAGENT_TARGET;
|
|
207
|
-
|
|
221
|
+
const changed = this.set({
|
|
208
222
|
class: 'subagent',
|
|
209
223
|
target,
|
|
210
224
|
span: `${toolCallId}:${index}`,
|
|
211
225
|
parent: toolCallId,
|
|
212
226
|
});
|
|
227
|
+
if (changed) this.ownerCallId = toolCallId;
|
|
228
|
+
return changed;
|
|
213
229
|
}
|
|
214
230
|
return false;
|
|
215
231
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import type { GoalHeaderState } from './goal.ts';
|
|
2
|
-
|
|
3
1
|
export type CheckState = 'pass' | 'fail' | 'running' | 'unknown';
|
|
4
2
|
export type WorkflowState = 'running' | 'blocked' | 'complete' | 'idle' | 'unknown';
|
|
5
3
|
export type WidgetColor =
|
|
@@ -193,11 +191,6 @@ export type StageValue =
|
|
|
193
191
|
|
|
194
192
|
export type SelectionState =
|
|
195
193
|
| { kind: 'openspec-task'; change: string; task: string }
|
|
196
|
-
| {
|
|
197
|
-
kind: 'goal';
|
|
198
|
-
status: GoalHeaderState['status'];
|
|
199
|
-
waiting: boolean;
|
|
200
|
-
}
|
|
201
194
|
| { kind: 'session-work'; intent: string; phase: 'active' | 'validation' }
|
|
202
195
|
| { kind: 'subject'; title: string }
|
|
203
196
|
| { kind: 'none' };
|