@@ -56,7 +56,7 @@ type testEmitter struct{}
5656func (testEmitter ) EmitMessages ([]st.ConversationMessage ) {}
5757func (testEmitter ) EmitStatus (st.ConversationStatus ) {}
5858func (testEmitter ) EmitScreen (string ) {}
59- func (testEmitter ) EmitError (_ string , _ string ) {}
59+ func (testEmitter ) EmitError (_ string , _ st. ErrorLevel ) {}
6060
6161// advanceFor is a shorthand for advanceUntil with a time-based condition.
6262func advanceFor (ctx context.Context , t * testing.T , mClock * quartz.Mock , total time.Duration ) {
@@ -798,6 +798,57 @@ func TestStatePersistence(t *testing.T) {
798798 messages := c .Messages ()
799799 assert .Len (t , messages , 1 )
800800 })
801+
802+ t .Run ("LoadState rejects unsupported version" , func (t * testing.T ) {
803+ ctx , cancel := context .WithTimeout (context .Background (), testTimeout )
804+ t .Cleanup (cancel )
805+
806+ tmpDir := t .TempDir ()
807+ stateFile := tmpDir + "/unsupported_version.json"
808+
809+ // Create state file with unsupported version
810+ unsupportedState := map [string ]interface {}{
811+ "version" : 999 , // Unsupported version
812+ "messages" : []interface {}{},
813+ "initial_prompt" : "" ,
814+ "initial_prompt_sent" : false ,
815+ }
816+ stateBytes , err := json .Marshal (unsupportedState )
817+ require .NoError (t , err )
818+ err = os .WriteFile (stateFile , stateBytes , 0o644 )
819+ require .NoError (t , err )
820+
821+ mClock := quartz .NewMock (t )
822+ agent := & testAgent {screen : "ready" }
823+ cfg := st.PTYConversationConfig {
824+ Clock : mClock ,
825+ SnapshotInterval : 100 * time .Millisecond ,
826+ ScreenStabilityLength : 200 * time .Millisecond ,
827+ AgentIO : agent ,
828+ Logger : slog .New (slog .NewTextHandler (io .Discard , nil )),
829+ FormatMessage : func (message string , userInput string ) string {
830+ return message
831+ },
832+ ReadyForInitialPrompt : func (message string ) bool {
833+ return message == "ready"
834+ },
835+ StatePersistenceConfig : st.StatePersistenceConfig {
836+ StateFile : stateFile ,
837+ LoadState : true ,
838+ SaveState : false ,
839+ },
840+ }
841+
842+ // Should not panic - logs error and continues with empty state
843+ c := st .NewPTY (ctx , cfg , & testEmitter {})
844+ c .Start (ctx )
845+
846+ advanceFor (ctx , t , mClock , 300 * time .Millisecond )
847+
848+ // Should have default initial message (version error causes fallback to empty state)
849+ messages := c .Messages ()
850+ assert .Len (t , messages , 1 )
851+ })
801852}
802853
803854func TestInitialPromptReadiness (t * testing.T ) {
@@ -1286,4 +1337,63 @@ func TestInitialPromptSent(t *testing.T) {
12861337 }
12871338 assert .True (t , found , "saved prompt should be sent when no new prompt provided" )
12881339 })
1340+
1341+ t .Run ("empty prompt from state is not restored" , func (t * testing.T ) {
1342+ ctx , cancel := context .WithTimeout (context .Background (), testTimeout )
1343+ t .Cleanup (cancel )
1344+
1345+ tmpDir := t .TempDir ()
1346+ stateFile := tmpDir + "/state.json"
1347+
1348+ // Create state file with empty prompt
1349+ emptyPromptState := st.AgentState {
1350+ Version : 1 ,
1351+ Messages : []st.ConversationMessage {},
1352+ InitialPrompt : "" , // Empty prompt
1353+ InitialPromptSent : false ,
1354+ }
1355+ stateBytes , err := json .Marshal (emptyPromptState )
1356+ require .NoError (t , err )
1357+ err = os .WriteFile (stateFile , stateBytes , 0o644 )
1358+ require .NoError (t , err )
1359+
1360+ mClock := quartz .NewMock (t )
1361+ agent := & testAgent {screen : "ready" }
1362+
1363+ cfg := st.PTYConversationConfig {
1364+ Clock : mClock ,
1365+ SnapshotInterval : 100 * time .Millisecond ,
1366+ ScreenStabilityLength : 200 * time .Millisecond ,
1367+ AgentIO : agent ,
1368+ Logger : discardLogger ,
1369+ FormatMessage : func (message string , userInput string ) string {
1370+ return message
1371+ },
1372+ ReadyForInitialPrompt : func (message string ) bool {
1373+ return message == "ready"
1374+ },
1375+ StatePersistenceConfig : st.StatePersistenceConfig {
1376+ StateFile : stateFile ,
1377+ LoadState : true ,
1378+ SaveState : false ,
1379+ },
1380+ }
1381+
1382+ c := st .NewPTY (ctx , cfg , & testEmitter {})
1383+ c .Start (ctx )
1384+
1385+ // Agent becomes ready
1386+ agent .setScreen ("ready" )
1387+
1388+ // Advance time to ensure any prompt would be sent
1389+ advanceFor (ctx , t , mClock , 500 * time .Millisecond )
1390+
1391+ // Verify no prompt was sent (should only have the initial screen message)
1392+ messages := c .Messages ()
1393+ for _ , msg := range messages {
1394+ if msg .Role == st .ConversationRoleUser {
1395+ t .Errorf ("Unexpected user message sent: %q (empty prompt should not be restored)" , msg .Message )
1396+ }
1397+ }
1398+ })
12891399}
0 commit comments