11import { describe , expect , spyOn , test } from "bun:test" ;
22import { defined } from "../../tests/helpers/defined.js" ;
3+ import { OPERATOR_ORIGINATED_FLAG } from "../agent/message-provenance.js" ;
4+ import { buildShellBackgroundMessage } from "../session/runtime-assembly.js" ;
35import {
46 FIXTURE_BUSY_SESSION ,
57 attachSessionBridge ,
@@ -17,15 +19,26 @@ import { badgeCount } from "./session-queue";
1719import { LIVE_ACTIVITY_WORDS } from "./session-chrome" ;
1820
1921describe ( "mapReactorLike" , ( ) => {
20- test ( "message.received → user" , ( ) => {
22+ test ( "operator-originated message.received → user" , ( ) => {
2123 expect (
2224 mapReactorLike ( {
2325 type : "message.received" ,
24- data : { message : { content : "hi" } } ,
26+ data : {
27+ message : { content : "hi" , flags : [ OPERATOR_ORIGINATED_FLAG ] } ,
28+ } ,
2529 } ) ,
2630 ) . toEqual ( [ { type : "user" , text : "hi" } ] ) ;
2731 } ) ;
2832
33+ test ( "system-originated message.received → system" , ( ) => {
34+ expect (
35+ mapReactorLike ( {
36+ type : "message.received" ,
37+ data : { message : { content : "hi" } } ,
38+ } ) ,
39+ ) . toEqual ( [ { type : "system" , text : "hi" } ] ) ;
40+ } ) ;
41+
2942 test ( "mapReactorLike tool.done types" , ( ) => {
3043 const mapped = mapReactorLike ( {
3144 type : "tool.done" ,
@@ -47,6 +60,44 @@ describe("mapReactorLike", () => {
4760} ) ;
4861
4962describe ( "attachSessionBridge" , ( ) => {
63+ test ( "background shell exit paints as a system row" , async ( ) => {
64+ await withTestRenderer (
65+ async ( h ) => {
66+ const shell = createAppShell ( h . renderer , {
67+ terminal : { columns : 80 , rows : 24 } ,
68+ wireKeys : false ,
69+ run : "idle" ,
70+ } ) ;
71+ const bridge = attachSessionBridge ( shell , createRecordingPort ( ) ) ;
72+ try {
73+ const message = buildShellBackgroundMessage ( {
74+ id : "sh_1" ,
75+ command : "sleep 0.1" ,
76+ exitCode : 0 ,
77+ timedOut : false ,
78+ output : "done" ,
79+ } ) ;
80+ bridge . handle ( {
81+ type : "message.received" ,
82+ data : { message } ,
83+ } ) ;
84+ expect ( shell . streamLog . filter ( ( r ) => r . role === "user" ) ) . toHaveLength (
85+ 0 ,
86+ ) ;
87+ expect (
88+ shell . streamLog . filter (
89+ ( r ) => r . role === "system" && r . text === message . content ,
90+ ) ,
91+ ) . toHaveLength ( 1 ) ;
92+ } finally {
93+ bridge . dispose ( ) ;
94+ shell . dispose ( ) ;
95+ }
96+ } ,
97+ { width : 80 , height : 24 } ,
98+ ) ;
99+ } ) ;
100+
50101 test ( "fixture paints user / assistant / tool through shell" , async ( ) => {
51102 await withTestRenderer (
52103 async ( h ) => {
@@ -1549,7 +1600,7 @@ describe("fleet-dry open-task drive (CL-7540)", () => {
15491600 bridge . handle ( { type : "inference.done" , data : { } } ) ;
15501601 }
15511602
1552- test ( "dry+open: fleet-0 settle drives once, keeps the run busy, and swallows the prompt" , async ( ) => {
1603+ test ( "dry+open: fleet-0 settle drives once, keeps the run busy, and paints the prompt as system " , async ( ) => {
15531604 await withTestRenderer (
15541605 async ( h ) => {
15551606 const shell = createAppShell ( h . renderer , {
@@ -1587,6 +1638,11 @@ describe("fleet-dry open-task drive (CL-7540)", () => {
15871638 expect ( shell . streamLog . filter ( ( r ) => r . role === "user" ) . length ) . toBe (
15881639 userRowsBefore ,
15891640 ) ;
1641+ expect (
1642+ shell . streamLog . filter (
1643+ ( r ) => r . role === "system" && r . text === prompt ,
1644+ ) ,
1645+ ) . toHaveLength ( 1 ) ;
15901646 settleToollessTurn ( bridge ) ;
15911647 expect ( drives ) . toBe ( 1 ) ;
15921648 } finally {
@@ -1966,7 +2022,7 @@ describe("fleet-dry open-task drive (CL-7540)", () => {
19662022 ) ;
19672023 } ) ;
19682024
1969- test ( "occupancy send abort drops the continuation echo so a later matching inbound paints " , async ( ) => {
2025+ test ( "occupancy send abort does not swallow a later matching inbound" , async ( ) => {
19702026 await withTestRenderer (
19712027 async ( h ) => {
19722028 const shell = createAppShell ( h . renderer , {
@@ -2001,8 +2057,13 @@ describe("fleet-dry open-task drive (CL-7540)", () => {
20012057 data : { message : { content : occupancy } } ,
20022058 } ) ;
20032059 expect ( shell . streamLog . filter ( ( r ) => r . role === "user" ) . length ) . toBe (
2004- userRowsAfterSubmit + 1 ,
2060+ userRowsAfterSubmit ,
20052061 ) ;
2062+ expect (
2063+ shell . streamLog . filter (
2064+ ( r ) => r . role === "system" && r . text === occupancy ,
2065+ ) ,
2066+ ) . toHaveLength ( 1 ) ;
20062067 } finally {
20072068 bridge . dispose ( ) ;
20082069 shell . dispose ( ) ;
0 commit comments